diff --git a/test/source.cs b/test/source.cs
index e69de29..b2aad3b 100644
--- a/test/source.cs
+++ b/test/source.cs
@@ -0,0 +1,259 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Compression;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using Microsoft.Win32;
+using Shadowsocks.Controller;
+
+namespace Shadowsocks.Util
+{
+ public struct BandwidthScaleInfo
+ {
+ public float value;
+ public string unitName;
+ public long unit;
+
+ public BandwidthScaleInfo(float value, string unitName, long unit)
+ {
+ this.value = value;
+ this.unitName = unitName;
+ this.unit = unit;
+ }
+ }
+
+ public static class Utils
+ {
+ private static string _tempPath = null;
+
+ // return path to store temporary files
+ public static string GetTempPath()
+ {
+ if (_tempPath == null)
+ {
+ try
+ {
+ Directory.CreateDirectory(Path.Combine(Application.StartupPath, "ss_win_temp"));
+ // don't use "/", it will fail when we call explorer /select xxx/ss_win_temp\xxx.log
+ _tempPath = Path.Combine(Application.StartupPath, "ss_win_temp");
+ }
+ catch (Exception e)
+ {
+ Logging.Error(e);
+ throw;
+ }
+ }
+ return _tempPath;
+ }
+
+ // return a full path with filename combined which pointed to the temporary directory
+ public static string GetTempPath(string filename)
+ {
+ return Path.Combine(GetTempPath(), filename);
+ }
+
+ public static void ReleaseMemory(bool removePages)
+ {
+ // release any unused pages
+ // making the numbers look good in task manager
+ // this is totally nonsense in programming
+ // but good for those users who care
+ // making them happier with their everyday life
+ // which is part of user experience
+ GC.Collect(GC.MaxGeneration);
+ GC.WaitForPendingFinalizers();
+ if (removePages)
+ {
+ // as some users have pointed out
+ // removing pages from working set will cause some IO
+ // which lowered user experience for another group of users
+ //
+ // so we do 2 more things here to satisfy them:
+ // 1. only remove pages once when configuration is changed
+ // 2. add more comments here to tell users that calling
+ // this function will not be more frequent than
+ // IM apps writing chat logs, or web browsers writing cache files
+ // if they're so concerned about their disk, they should
+ // uninstall all IM apps and web browsers
+ //
+ // please open an issue if you're worried about anything else in your computer
+ // no matter it's GPU performance, monitor contrast, audio fidelity
+ // or anything else in the task manager
+ // we'll do as much as we can to help you
+ //
+ // just kidding
+ SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
+ (UIntPtr)0xFFFFFFFF,
+ (UIntPtr)0xFFFFFFFF);
+ }
+ }
+
+ public static string UnGzip(byte[] buf)
+ {
+ byte[] buffer = new byte[1024];
+ int n;
+ using (MemoryStream sb = new MemoryStream())
+ {
+ using (GZipStream input = new GZipStream(new MemoryStream(buf),
+ CompressionMode.Decompress,
+ false))
+ {
+ while ((n = input.Read(buffer, 0, buffer.Length)) > 0)
+ {
+ sb.Write(buffer, 0, n);
+ }
+ }
+ return System.Text.Encoding.UTF8.GetString(sb.ToArray());
+ }
+ }
+
+ public static string FormatBandwidth(long n)
+ {
+ var result = GetBandwidthScale(n);
+ return $"{result.value:0.##}{result.unitName}";
+ }
+
+ public static string FormatBytes(long bytes)
+ {
+ const long K = 1024L;
+ const long M = K * 1024L;
+ const long G = M * 1024L;
+ const long T = G * 1024L;
+ const long P = T * 1024L;
+ const long E = P * 1024L;
+
+ if (bytes >= P * 990)
+ return (bytes / (double)E).ToString("F5") + "EiB";
+ if (bytes >= T * 990)
+ return (bytes / (double)P).ToString("F5") + "PiB";
+ if (bytes >= G * 990)
+ return (bytes / (double)T).ToString("F5") + "TiB";
+ if (bytes >= M * 990)
+ {
+ return (bytes / (double)G).ToString("F4") + "GiB";
+ }
+ if (bytes >= M * 100)
+ {
+ return (bytes / (double)M).ToString("F1") + "MiB";
+ }
+ if (bytes >= M * 10)
+ {
+ return (bytes / (double)M).ToString("F2") + "MiB";
+ }
+ if (bytes >= K * 990)
+ {
+ return (bytes / (double)M).ToString("F3") + "MiB";
+ }
+ if (bytes > K * 2)
+ {
+ return (bytes / (double)K).ToString("F1") + "KiB";
+ }
+ return bytes.ToString() + "B";
+ }
+
+ ///
+ /// Return scaled bandwidth
+ ///
+ /// Raw bandwidth
+ ///
+ /// The BandwidthScaleInfo struct
+ ///
+ public static BandwidthScaleInfo GetBandwidthScale(long n)
+ {
+ long scale = 1;
+ float f = n;
+ string unit = "B";
+ if (f > 1024)
+ {
+ f = f / 1024;
+ scale <<= 10;
+ unit = "KiB";
+ }
+ if (f > 1024)
+ {
+ f = f / 1024;
+ scale <<= 10;
+ unit = "MiB";
+ }
+ if (f > 1024)
+ {
+ f = f / 1024;
+ scale <<= 10;
+ unit = "GiB";
+ }
+ if (f > 1024)
+ {
+ f = f / 1024;
+ scale <<= 10;
+ unit = "TiB";
+ }
+ return new BandwidthScaleInfo(f, unit, scale);
+ }
+
+ public static RegistryKey OpenRegKey(string name, bool writable, RegistryHive hive = RegistryHive.CurrentUser)
+ {
+ // we are building x86 binary for both x86 and x64, which will
+ // cause problem when opening registry key
+ // detect operating system instead of CPU
+ if (name.IsNullOrEmpty()) throw new ArgumentException(nameof(name));
+ try
+ {
+ RegistryKey userKey = RegistryKey.OpenBaseKey(hive,
+ Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32)
+ .OpenSubKey(name, writable);
+ return userKey;
+ }
+ catch (ArgumentException ae)
+ {
+ MessageBox.Show("OpenRegKey: " + ae.ToString());
+ return null;
+ }
+ catch (Exception e)
+ {
+ Logging.LogUsefulException(e);
+ return null;
+ }
+ }
+
+ public static bool IsWinVistaOrHigher()
+ {
+ return Environment.OSVersion.Version.Major > 5;
+ }
+
+ [DllImport("kernel32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool SetProcessWorkingSetSize(IntPtr process,
+ UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize);
+
+
+ // See: https://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx
+ public static bool IsSupportedRuntimeVersion()
+ {
+ /*
+ * +-----------------------------------------------------------------+----------------------------+
+ * | Version | Value of the Release DWORD |
+ * +-----------------------------------------------------------------+----------------------------+
+ * | .NET Framework 4.6.2 installed on Windows 10 Anniversary Update | 394802 |
+ * | .NET Framework 4.6.2 installed on all other Windows OS versions | 394806 |
+ * +-----------------------------------------------------------------+----------------------------+
+ */
+ const int minSupportedRelease = 394802;
+
+ const string subkey = @"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\";
+ using (var ndpKey = OpenRegKey(subkey, false, RegistryHive.LocalMachine))
+ {
+ if (ndpKey?.GetValue("Release") != null)
+ {
+ var releaseKey = (int)ndpKey.GetValue("Release");
+
+ if (releaseKey >= minSupportedRelease)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file