private void Run(int m)
        {
            string mask = Masks[m];

            // resize mask to next % 12 for faster jenkins
            byte[] maskdata = Encoding.ASCII.GetBytes(mask);
            Array.Resize(ref maskdata, (mask.Length + (12 - mask.Length % 12) % 12));

            // calculate the indicies of the wildcard chars
            byte[] maskoffsets = Enumerable.Range(0, mask.Length).Where(i => mask[i] == '%').Select(i => (byte)i).ToArray();
            if (maskoffsets.Length > 12 * (IsMirrored ? 2 : 1))
            {
                Console.WriteLine($"Error: Too many wildcards - maximum is {12 * (IsMirrored ? 2 : 1)}. `{mask}`");
                return;
            }

            // mirrored is two indentical masks so must have an even count of wildcards
            if (IsMirrored && maskoffsets.Length % 2 != 0)
            {
                Console.WriteLine($"Error: Mirrored flag used with an odd number of wildcards. `{mask}`");
                return;
            }

            // reorder mirrored indices for faster permutation computing
            if (IsMirrored)
            {
                int    halfcount = maskoffsets.Length / 2;
                byte[] temp      = new byte[maskoffsets.Length];
                for (int i = 0; i < halfcount; i++)
                {
                    temp[i * 2]       = maskoffsets[i];
                    temp[(i * 2) + 1] = maskoffsets[halfcount + i];
                }
                maskoffsets = temp;
            }

            // replace kernel placeholders - faster than using buffers
            KernelWriter kernel = new KernelWriter(Properties.Resources.Jenkins);

            kernel.ReplaceArray("DATA", maskdata);
            kernel.ReplaceArray("OFFSETS", maskoffsets);
            kernel.ReplaceArray("HASHES", TargetHashes);
            kernel.Replace("DATA_SIZE_REAL", mask.Length);
            kernel.ReplaceOffsetArray(TargetHashes);

            // load CL - filter contexts to the specific device type
            MultiCL cl = new MultiCL(ComputeDevice);

            Console.WriteLine($"Loading kernel - {TargetHashes.Count - 1} hashes. This may take a minute...");
            cl.SetKernel(kernel.ToString(), IsMirrored ? "BruteforceMirrored" : "Bruteforce");

            // limit workload to MAX_WORKSIZE and use an on-device loop to breach that value
            BigInteger combinations = BigInteger.Pow(39, maskoffsets.Length / (IsMirrored ? 2 : 1));             // total combinations
            uint       loops        = (uint)Math.Floor(Math.Exp(BigInteger.Log(combinations) - BigInteger.Log(GLOBAL_WORKSIZE)));

            // Start the work
            Console.WriteLine($"Starting Jenkins Hashing :: {combinations} combinations ");
            Stopwatch time = Stopwatch.StartNew();

            // output buffer arg
            var resultArg = CLArgument <ulong> .CreateReturn(TargetHashes.Count);

            // set up internal loop of GLOBAL_WORKSIZE
            if (loops > 0)
            {
                // loop size, index offset, output buffer
                cl.SetParameter(loops, (ulong)0, resultArg);
                Enqueue(cl.InvokeReturn <ulong>(GLOBAL_WORKSIZE, TargetHashes.Count));
                combinations -= loops * GLOBAL_WORKSIZE;
            }

            // process remaining
            if (combinations > 0)
            {
                // loop size, index offset, output buffer
                cl.SetParameter(1, (ulong)(loops * GLOBAL_WORKSIZE), resultArg);
                Enqueue(cl.InvokeReturn <ulong>((long)combinations, TargetHashes.Count));
            }

            time.Stop();
            Console.WriteLine($"Completed in {time.Elapsed.TotalSeconds.ToString("0.00")} secs");
            Validate(mask, maskoffsets);
        }
        private void Run(int m)
        {
            string mask = Masks[m];

            ParseHashes(mask);

            // handle templates without wildcards
            if (!mask.Contains('%'))
            {
                ResultQueue.Enqueue(0);
                Validate(mask, new byte[0]);
                return;
            }

            // resize mask to next % 12 for faster jenkins
            byte[] maskdata = Encoding.ASCII.GetBytes(mask);
            Array.Resize(ref maskdata, (int)AlignTo(mask.Length, 12));

            // calculate the indicies of the wildcard chars
            byte[] maskoffsets = Enumerable.Range(0, mask.Length).Where(i => mask[i] == '%').Select(i => (byte)i).ToArray();
            if (maskoffsets.Length > 12 * (IsMirrored ? 2 : 1))
            {
                Console.WriteLine($"Error: Too many wildcards - maximum is {12 * (IsMirrored ? 2 : 1)}. `{mask}`");
                return;
            }

            // mirrored is two indentical masks so must have an even count of wildcards
            if (IsMirrored && maskoffsets.Length % 2 != 0)
            {
                Console.WriteLine($"Error: Mirrored flag used with an odd number of wildcards. `{mask}`");
                return;
            }

            // reorder mirrored indices for faster permutation computing
            if (IsMirrored)
            {
                int    halfcount = maskoffsets.Length / 2;
                byte[] temp      = new byte[maskoffsets.Length];
                for (int i = 0; i < halfcount; i++)
                {
                    temp[i * 2]       = maskoffsets[i];
                    temp[(i * 2) + 1] = maskoffsets[halfcount + i];
                }
                maskoffsets = temp;
            }

            // replace kernel placeholders - faster than using buffers
            KernelWriter kernel = new KernelWriter(Properties.Resources.Jenkins);

            kernel.ReplaceArray("DATA", maskdata);
            kernel.ReplaceArray("OFFSETS", maskoffsets);
            kernel.Replace("DATA_SIZE_REAL", mask.Length);
            kernel.ReplaceOffsetArray(TargetHashes);

            // load CL - filter contexts to the specific device type
            MultiCL cl = new MultiCL(ComputeDevice);

            Console.WriteLine($"Loading kernel - {TargetHashes.Count - 1} hashes");
            cl.SetKernel(kernel.ToString(), IsMirrored ? "BruteforceMirrored" : "Bruteforce");

            // output buffer arg
            int bufferSize = (TargetHashes.Count + (8 - TargetHashes.Count % 8) % 8);             // buffer should be 64 byte aligned
            var resultArg  = CLArgument <ulong> .CreateReturn(bufferSize);

            // alignment calculations
            BigInteger COMBINATIONS = BigInteger.Pow(39, maskoffsets.Length / (IsMirrored ? 2 : 1));             // calculate combinations
            long       GLOBAL_WORKSIZE = 0, LOOPS = 0;
            long       WARP_SIZE = cl.WarpSize;

            // Start the work
            Console.WriteLine($"Starting Jenkins Hashing :: {COMBINATIONS} combinations ");
            Stopwatch time = Stopwatch.StartNew();

            COMBINATIONS = AlignTo(COMBINATIONS, WARP_SIZE);             // align to the warp size

            // nvidia + too many threads + lots of wildcards = out of resources
            if (cl.HasNVidia && maskoffsets.Length > 7)
            {
                BASE_GLOBAL_WORKSIZE -= (maskoffsets.Length - 7) * 613566752;
            }

            string     total     = COMBINATIONS.ToString("#,##0,,M", CultureInfo.InvariantCulture);
            var        TOTAL     = COMBINATIONS;
            BigInteger COMPLETED = 0;

            if (COMBINATIONS > BASE_GLOBAL_WORKSIZE)
            {
                GLOBAL_WORKSIZE = (long)ReduceTo(BASE_GLOBAL_WORKSIZE, WARP_SIZE);
                LOOPS           = (uint)Math.Floor(Math.Exp(BigInteger.Log(COMBINATIONS) - BigInteger.Log(GLOBAL_WORKSIZE)));

                // set up internal loop of GLOBAL_WORKSIZE
                for (uint i = 0; i < LOOPS; i++)
                {
                    // index offset, count, output buffer
                    cl.SetParameter((ulong)(i * GLOBAL_WORKSIZE), resultArg);

                    // my card crashes if it is going full throttle and I forcibly exit the kernel
                    // this overrides the default exit behaviour and waits for a break in GPU processing before exiting
                    // - if the exit event is fired twice it'll just force close
                    CleanExitHandler.IsProcessing = ComputeDevice.HasFlag(ComputeDeviceTypes.Gpu);
                    Enqueue(cl.InvokeReturn <ulong>(GLOBAL_WORKSIZE, null, bufferSize));

                    CleanExitHandler.ProcessExit();

                    COMBINATIONS -= GLOBAL_WORKSIZE;
                    COMPLETED    += GLOBAL_WORKSIZE;

                    if ((BigInteger)time.Elapsed.TotalSeconds != 0)
                    {
                        string completed  = (i * GLOBAL_WORKSIZE).ToString("#,##0,,M", CultureInfo.InvariantCulture);
                        var    speed      = COMPLETED / (BigInteger)time.Elapsed.TotalSeconds;
                        string hps        = speed.ToString("#,##0,,M", CultureInfo.InvariantCulture);
                        string percentage =
                            ((double)COMPLETED / (double)TOTAL).ToString("P4", CultureInfo.InvariantCulture);
                        BigInteger left         = TOTAL - COMPLETED;
                        var        seconds_left = (left / speed);
                        TimeSpan   t            = TimeSpan.FromSeconds((double)seconds_left);
                        string     eta          = $"{(int)t.TotalHours}:{t:mm}:{t:ss}";
                        Console.WriteLine("{0} out of {1} completed ({2}), {3} hash/sec, ETA: {4}", completed, total,
                                          percentage, hps, eta);
                    }
                }
            }

            if (COMBINATIONS > 0)
            {
                // index offset, count, output buffer
                cl.SetParameter((ulong)(LOOPS * GLOBAL_WORKSIZE), resultArg);

                GLOBAL_WORKSIZE = (long)AlignTo(COMBINATIONS, WARP_SIZE);

                CleanExitHandler.IsProcessing = ComputeDevice.HasFlag(ComputeDeviceTypes.Gpu);
                Enqueue(cl.InvokeReturn <ulong>(GLOBAL_WORKSIZE, null, bufferSize));
                CleanExitHandler.ProcessExit();
            }

            time.Stop();
            Console.WriteLine($"Completed in {time.Elapsed.TotalSeconds.ToString("0.00")} secs");
            Validate(mask, maskoffsets);

            CleanExitHandler.IsProcessing = false;
        }
        private void Run(int m)
        {
            string mask = Masks[m];

            // handle templates without wildcards
            if (!mask.Contains('%'))
            {
                ResultQueue.Enqueue(0);
                Validate(mask, new byte[0]);
                return;
            }

            byte[] maskdata = Encoding.ASCII.GetBytes(mask);

            // calculate the indicies of the wildcard chars
            byte[] maskoffsets = Enumerable.Range(0, mask.Length).Where(i => mask[i] == '%').Select(i => (byte)i).ToArray();
            if (maskoffsets.Length > 12)
            {
                Console.WriteLine($"Error: Too many wildcards - maximum is {12}. `{mask}`");
                return;
            }

            // replace kernel placeholders - faster than using buffers
            KernelWriter kernel = new KernelWriter(Properties.Resources.Table);

            kernel.ReplaceArray("DATA", maskdata);
            kernel.ReplaceArray("OFFSETS", maskoffsets);
            kernel.Replace("DATA_SIZE_REAL", mask.Length);
            kernel.ReplaceOffsetArray(TargetHashes);

            // load CL - filter contexts to the specific device type
            MultiCL cl = new MultiCL(ComputeDevice);

            Console.WriteLine($"Loading kernel - {TargetHashes.Count - 1} hashes");
            cl.SetKernel(kernel.ToString(), "Bruteforce");

            // output buffer arg
            int bufferSize = (TargetHashes.Count + (8 - TargetHashes.Count % 8) % 8);             // buffer should be 64 byte aligned
            var resultArg  = CLArgument <ulong> .CreateReturn(bufferSize);

            // alignment calculations
            BigInteger COMBINATIONS = BigInteger.Pow(26, maskoffsets.Length);             // calculate combinations
            long       GLOBAL_WORKSIZE = 0, LOOPS = 0;
            long       WARP_SIZE = cl.WarpSize;

            // Start the work
            Console.WriteLine($"Starting DB Table Hashing :: {COMBINATIONS} combinations ");
            Stopwatch time = Stopwatch.StartNew();

            COMBINATIONS = AlignTo(COMBINATIONS, WARP_SIZE);             // align to the warp size

            // nvidia + too many threads + lots of wildcards = out of resources
            if (cl.HasNVidia && maskoffsets.Length > 7)
            {
                BASE_GLOBAL_WORKSIZE -= (maskoffsets.Length - 7) * 613566752;
            }

            if (COMBINATIONS > BASE_GLOBAL_WORKSIZE)
            {
                GLOBAL_WORKSIZE = (long)ReduceTo(BASE_GLOBAL_WORKSIZE, WARP_SIZE);
                LOOPS           = (uint)Math.Floor(Math.Exp(BigInteger.Log(COMBINATIONS) - BigInteger.Log(GLOBAL_WORKSIZE)));

                // set up internal loop of GLOBAL_WORKSIZE
                for (uint i = 0; i < LOOPS; i++)
                {
                    // index offset, count, output buffer
                    cl.SetParameter((ulong)(i * GLOBAL_WORKSIZE), resultArg);

                    // my card crashes if it is going full throttle and I forcibly exit the kernel
                    // this overrides the default exit behaviour and waits for a break in GPU processing before exiting
                    // - if the exit event is fired twice it'll just force close
                    CleanExitHandler.IsProcessing = ComputeDevice.HasFlag(ComputeDeviceTypes.Gpu);
                    Enqueue(cl.InvokeReturn <ulong>(GLOBAL_WORKSIZE, null, bufferSize));
                    CleanExitHandler.ProcessExit();

                    if (i == 0)
                    {
                        LogEstimation(LOOPS, time.Elapsed.TotalSeconds);
                    }

                    COMBINATIONS -= GLOBAL_WORKSIZE;
                }
            }

            if (COMBINATIONS > 0)
            {
                // index offset, count, output buffer
                cl.SetParameter((ulong)(LOOPS * GLOBAL_WORKSIZE), resultArg);

                GLOBAL_WORKSIZE = (long)AlignTo(COMBINATIONS, WARP_SIZE);

                CleanExitHandler.IsProcessing = ComputeDevice.HasFlag(ComputeDeviceTypes.Gpu);
                Enqueue(cl.InvokeReturn <ulong>(GLOBAL_WORKSIZE, null, bufferSize));
                CleanExitHandler.ProcessExit();
            }

            time.Stop();
            Console.WriteLine($"Completed in {time.Elapsed.TotalSeconds.ToString("0.00")} secs");
            Validate(mask, maskoffsets);

            CleanExitHandler.IsProcessing = false;
        }