A fast, iterative, correct approach to Stackblur, resulting in a very smooth and high-quality output, with no edge bleeding

Overview

A fast, iterative, correct approach to Stackblur, resulting in a very smooth and high-quality output, with no edge bleeding.

This crate implements a tweaked version of the Stackblur algorithm requiring radius * 2 + 2 elements of space rather than radius * 2 + 1, which is a small tradeoff for much-increased visual quality.

The algorithm is exposed as an iterator (StackBlur) that can wrap any other iterator that yields elements of StackBlurrable. The StackBlur will then yield elements blurred by the specified radius.

Benefits of this crate

Stackblur is essentially constant-time. Regardless of the radius, it always performs only 1 scan over the input iterator and outputs exactly the same amount of elements.

Additionally, it produces results that are comparable to slow and expensive Gaussian blurs. As opposed to box blur which uses a basic rolling average, Stackblur uses a weighted average where each output pixel is affected more strongly by the inputs that were closest to it.

Despite that, Stackblur does not perform much worse compared to naive box blurs, and is quite cheap compared to full Gaussian blurs, at least for the CPU. The implementation in this crate will most likely beat most unoptimized blurs you can find on crates.io, as well as some optimized ones, and it is extremely flexible and generic.

For a full explanation of the improvements made to the Stackblur algorithm, see the iter module.

KjK33N7k6W.mp4

(In the above video, stackblur-iter is the centered blur, whereas the full-width one is another stackblur crate.)

Comparison to the stackblur crate

stackblur suffers from edge bleeding and flexibility problems. For example, it can only operate on buffers of 32-bit integers, and expects them to be packed linear ARGB pixels. Additionally, it cannot operate on a 2D subslice of a buffer (like imgref allows for this crate), and it does not offer any streaming iterators or documentation. And it also only supports a blur radius of up to 255.

Usage

Aside from StackBlurrable and StackBlur which host their own documentation, there are helper functions like blur and blur_argb that can be used to interact with 2D image buffers, due to the fact that doing so manually involves unsafe code (if you want no-copy).

See the full documentation for more.

Comments
  • Blurred image is grayed out, despite the input not being so

    Blurred image is grayed out, despite the input not being so

    So I'm using your crate in my app and hitting an issue while trying to blur a gdk_pixbuf::Pixbufs, that is for certain images the blurred result is grayed out. I believe that it is related to the size of the image, but I'm not entirely too sure. This is an example that exhibits this issue

    use gdk_pixbuf::{Colorspace, Pixbuf};
    use stackblur_iter::{blur_argb, imgref::ImgRefMut};
    
    fn main() {
        let pixbuf = Pixbuf::from_file("unknown.png").unwrap();
        assert!(pixbuf.n_channels() == 3); // This assertion exists because the Pixbuf I'm blurring in my program should always lack an alpha channel
    
        let pixels = pixbuf.pixel_bytes().unwrap().to_vec();
        let width = pixbuf.width() as usize;
        let height = pixbuf.height() as usize;
    
        let mut pixels = match pixbuf.n_channels() {
            3 => blur_rgb(pixels, width, height, 5),
            _ => unreachable!(),
        };
    
        Pixbuf::from_mut_slice(
            &mut pixels,
            Colorspace::Rgb,
            false,
            8,
            pixbuf.width(),
            pixbuf.height(),
            pixbuf.rowstride(),
        )
        .savev("blurred.png", "png", &[])
        .unwrap();
    }
    
    fn blur_rgb(pixels: Vec<u8>, width: usize, height: usize, radius: usize) -> Vec<u8> {
        assert!(
            pixels.len() % 3 == 0,
            "The pixel buffer's length should be a multiple of 3, but it was '{}'.",
            pixels.len()
        );
    
        let mut pixels = pixels
            .chunks_exact(3)
            .map(|chunk| {
                let red = chunk[0];
                let green = chunk[1];
                let blue = chunk[2];
    
                u32::from_be_bytes([0xff, red, green, blue])
            })
            .collect::<Vec<_>>();
    
        let mut img = ImgRefMut::new(&mut pixels, width, height);
    
        blur_argb(&mut img, radius);
    
        pixels
            .into_iter()
            .flat_map(|pixel| {
                let [_, r, g, b] = pixel.to_be_bytes();
    
                [r, g, b]
            })
            .collect()
    }
    
    An example image and the result after the above program is ran

    original image

    becomes

    blurred

    ran through the above program

    Cargo.toml for the above program
    [package]
    name = "stackblurtest"
    version = "0.1.0"
    edition = "2021"
    
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    
    [dependencies]
    stackblur-iter = { git = "https://github.com/LoganDark/stackblur-iter/", rev = "53be4ee685f2bc1fbbb79b9ab02ee6679de85694" }
    gdk-pixbuf = "0.15.11"
    

    The Pixbuf mentioned above stores its data in an [rgb][rgb][rgb]... format and I'm converting it to ARGB u32s as that's pretty convenient because it allows me to use the blur_argb method provided by your crate and not have to write my own RGB type.

    For the record I don't believe this to be an issue with me using 0xff for every alpha byte in the intermediary buffer as I replaced it with a randomly generated byte using the rand crate and the output didn't look like it changed.

    opened by RealKC 17
  • `blur_argb` upsets Miri

    `blur_argb` upsets Miri

    This snippet fails due to undefined behavior when run under miri:

    use stackblur_iter::imgref::ImgRefMut;
    
    fn main() {
        let mut buf = vec![
            0, 0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0,
            0, 0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0,
            0, 0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0,
        ];
    
        let mut img_ref = ImgRefMut::new(&mut buf, 3, 3);
        stackblur_iter::blur_argb(&mut img_ref, 1);
    }
    

    stackblur-iter = "0.1.5" rustc --version: rustc 1.64.0-nightly (f8588549c 2022-07-18)

    Error:

    $ cargo miri run
        Finished dev [unoptimized + debuginfo] target(s) in 0.13s
         Running `/Users/marko/.rustup/toolchains/nightly-x86_64-apple-darwin/bin/cargo-miri /Users/marko/.target-cargo/miri/x86_64-apple-darwin/debug/test-stackblur`
    error: Undefined Behavior: trying to reborrow from <3782> for SharedReadOnly permission at alloc1660[0x0], but that tag does not exist in the borrow stack for this location
       --> /Users/marko/.cargo/registry/src/github.com-1ecc6299db9ec823/imgref-iter-0.3.3/src/iter/windows/ptr.rs:135:13
        |
    135 |         let len = (*self.0).len();
        |                   ^^^^^^^^^^^^^^^
        |                   |
        |                   trying to reborrow from <3782> for SharedReadOnly permission at alloc1660[0x0], but that tag does not exist in the borrow stack for this location
        |                   this error occurs as part of a reborrow at alloc1660[0x0..0xc]
        |
        = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
        = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
    help: <3782> was created by a retag at offsets [0x0..0x90]
       --> src/main.rs:11:5
        |
    11  |     stackblur_iter::blur_argb(&mut img_ref, 1);
        |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    help: <3782> was later invalidated at offsets [0x8..0xc]
       --> src/main.rs:11:5
        |
    11  |     stackblur_iter::blur_argb(&mut img_ref, 1);
        |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        = note: backtrace:
        = note: inside `imgref_iter::iter::windows::ptr::IterWindowsPtr::<u32>::window` at /Users/marko/.cargo/registry/src/github.com-1ecc6299db9ec823/imgref-iter-0.3.3/src/iter/windows/ptr.rs:135:13
        = note: inside closure at /Users/marko/.cargo/registry/src/github.com-1ecc6299db9ec823/imgref-iter-0.3.3/src/iter/windows/ptr.rs:145:51
        = note: inside `std::option::Option::<usize>::map::<imgref_iter::iter::generic::ptr::IterPtr<u32>, [closure@<imgref_iter::iter::windows::ptr::IterWindowsPtr<u32> as std::iter::Iterator>::next::{closure#0}]>` at /Users/marko/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs:929:29
        = note: inside `<imgref_iter::iter::windows::ptr::IterWindowsPtr<u32> as std::iter::Iterator>::next` at /Users/marko/.cargo/registry/src/github.com-1ecc6299db9ec823/imgref-iter-0.3.3/src/iter/windows/ptr.rs:145:3
        = note: inside `<imgref_iter::iter::windows::IterWindows<u32> as std::iter::Iterator>::next` at /Users/marko/.cargo/registry/src/github.com-1ecc6299db9ec823/imgref-iter-0.3.3/src/iter/windows/mod.rs:43:3
        = note: inside `<std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>> as std::iter::adapters::zip::ZipImpl<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>>>::next` at /Users/marko/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/iter/adapters/zip.rs:155:21
        = note: inside `<std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>> as std::iter::Iterator>::next` at /Users/marko/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/iter/adapters/zip.rs:84:9
        = note: inside `<for<'r> fn(&'r mut std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>>) -> std::option::Option<<std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>> as std::iter::Iterator>::Item> {<std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>> as std::iter::Iterator>::next} as std::ops::FnOnce<(&mut std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>>,)>>::call_once - shim(for<'r> fn(&'r mut std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>>) -> std::option::Option<<std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>> as std::iter::Iterator>::Item> {<std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>> as std::iter::Iterator>::next})` at /Users/marko/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:248:5
        = note: inside `std::iter::adapters::chain::and_then_or_clear::<std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>>, (imgref_iter::iter::generic::ptr::IterPtrMut<u32>, imgref_iter::iter::generic::Iter<u32>), for<'r> fn(&'r mut std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>>) -> std::option::Option<<std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>> as std::iter::Iterator>::Item> {<std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>> as std::iter::Iterator>::next}>` at /Users/marko/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/iter/adapters/chain.rs:287:13
        = note: inside `<std::iter::Chain<std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>>, std::iter::Zip<imgref_iter::iter::windows::ptr::IterWindowsPtrMut<u32>, imgref_iter::iter::windows::IterWindows<u32>>> as std::iter::Iterator>::next` at /Users/marko/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/iter/adapters/chain.rs:50:9
        = note: inside `stackblur_iter::blur::<u32, stackblur_iter::color::Argb<stackblur_iter::color::serial::StackBlurrableU32>, [closure@stackblur_iter::blur_argb::{closure#0}], fn(stackblur_iter::color::Argb<stackblur_iter::color::serial::StackBlurrableU32>) -> u32 {stackblur_iter::color::Argb::<stackblur_iter::color::serial::StackBlurrableU32>::to_u32}>` at /Users/marko/.cargo/registry/src/github.com-1ecc6299db9ec823/stackblur-iter-0.1.5/src/lib.rs:88:23
        = note: inside `stackblur_iter::blur_argb` at /Users/marko/.cargo/registry/src/github.com-1ecc6299db9ec823/stackblur-iter-0.1.5/src/lib.rs:233:2
    note: inside `main` at src/main.rs:11:5
       --> src/main.rs:11:5
        |
    11  |     stackblur_iter::blur_argb(&mut img_ref, 1);
        |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
    
    error: aborting due to previous error
    
    bug help wanted 
    opened by overdrivenpotato 10
  • Rayon+SIMD support

    Rayon+SIMD support

    Combines #4 and #5

    Closes #3

    Using both at the same time now actually outperforms rayon on its own.

    This is surprising.

    Currently a lane count of 8 is the fastest on my machine, but this is tunable.

    This is all thanks to imgref-iter's experimental SIMD iterators.

    opened by LoganDark 6
  • Feature Request: Multithreading (`rayon`)

    Feature Request: Multithreading (`rayon`)

    It's great to see a correct stackblur implementation in Rust! I switched out the algorithm I use in i3lockr with yours and it takes about ~100 ms whereas the multithreaded C version of stackblur I was using takes ~30 ms. However, the C version is naive and clobbers the gamma. The 100 ms is including sRGB to linear round trip. I expect with multithreading yours may be very close to 30 ms, if not faster.

    enhancement 
    opened by owenthewizard 6
  • SIMD support

    SIMD support

    ~~This makes the library slower in single-threaded. I doubt it will be any different in multi-threaded. I implemented it on top of the rayon branch but it's not going to actually support rayon until SIMD is a speedup, which it is apparently not.~~

    ~~Would appreciate help getting it to be faster.~~

    New performance analysis TBD

    opened by LoganDark 2
  • Rayon support

    Rayon support

    I made changes in imgref-iter to make this possible.

    Behold:

    stackblur_1024       ... bench:  18,514,600 ns/iter (+/- 3,095,887)
    stackblur_128        ... bench:  11,743,780 ns/iter (+/- 2,533,038)
    stackblur_16         ... bench:  11,109,190 ns/iter (+/- 1,912,941)
    blur_srgb_1024       ... bench:  17,162,590 ns/iter (+/- 3,337,943)
    blur_srgb_128        ... bench:  14,260,590 ns/iter (+/- 2,214,481)
    blur_srgb_16         ... bench:  13,107,140 ns/iter (+/- 2,408,900)
    par_blur_srgb_1024   ... bench:   4,309,255 ns/iter (+/- 352,918)
    par_blur_srgb_128    ... bench:   3,979,880 ns/iter (+/- 305,431)
    par_blur_srgb_16     ... bench:   4,063,585 ns/iter (+/- 310,803)
    

    Fixes #3

    enhancement 
    opened by LoganDark 2
  • Implement fast division

    Implement fast division

    So there's this thing called libdivide that I don't fully understand, but basically it makes it possible to perform integer divides without actually using division instructions, which is a lot faster.

    The fastdivide crate has a really good explanation:

    Division is a very costly operation for your CPU. You may have noticed that when the divisor is known at compile time, your compiler transforms the operations into a cryptic combination of a multiplication and bitshift.

    The key idea is that, rather than computing

    N / D
    

    It is faster to compute (with k sufficiently large)

    N * ( 2^k / D ) / (2^k)
    

    If D is known in advance, (2^k / D) can be precomputed by the compiler.

    Unfortunately if the divisor is unknown at compile time, the compiler cannot use this trick.

    The point of fastdivide is to apply the same trick by letting you precompute a DivideU64 object.

    Dividing takes up a lot of the CPU time spent blurring. If divisions can be eliminated, the blur could become a lot faster.

    enhancement 
    opened by LoganDark 1
Releases(v0.2.0)
  • v0.2.0(Aug 13, 2022)

    This is a breaking change because of the huge MSRV hike. stackblur-iter now requires the very latest stable (1.63) for the latest version of imgref-iter. But it should now pass Miri.

    Fixes #11

    Source code(tar.gz)
    Source code(zip)
  • v0.1.6(Aug 13, 2022)

    Obtaining the column reader before writing to the rows is UB because writing to the rows invalidates the column reader's borrow.

    This release fixes that. It does not update imgref-iter yet because that will be a breaking change (due to the huge MSRV hike). The version of imgref-iter used here does not pass Miri yet (the latest version does).

    Source code(tar.gz)
    Source code(zip)
  • v0.1.5(Jul 18, 2022)

  • v0.1.4(Jul 18, 2022)

    Using the new SIMD iterators in imgref-iter, it's possible to implement SIMD version of stackblur. Right now SIMD doesn't offer too much of a benefit, but it is still a benefit nonetheless, and rayon definitely offers a huge benefit.

    This has been sitting around in PR hell for far too long, and I don't like managing multiple disparate branches, so it's about time this gets merged and released.

    Source code(tar.gz)
    Source code(zip)
  • v0.1.3(Jun 18, 2022)

    I actually have fixed this before, but it was lost in a commit that I decided not to push (the precursor to imgref-iter). I forgot that it included this fix, and therefore forgot to test radius zero again before publishing.

    Quite upset about this one, honestly.

    Source code(tar.gz)
    Source code(zip)
  • v0.1.2(Jun 18, 2022)

    Small optimization/cleanup release here.

    • Fixed StackBlur behavior with repeating iterators. It used to just continue without returning a None. Now it properly returns a None before continuing.

    • Switched to new imgref-iter iterators. These are relatively immature (I made them in about 4 hours) but they simplify the implementation of blur_horiz, blur_vert and blur massively, as well as possibly benefitting the ecosystem of crates that use imgref, since anyone can use the crate.

    • blur has been optimized slightly to avoid allocating more than one deque. It used to allocate two because it called blur_horiz and blur_vert individually.

    The perf increase is barely anything. Larger increases are planned from things like rayon and SIMD.

    Source code(tar.gz)
    Source code(zip)
  • v0.1.1(Jun 16, 2022)

    All internal changes and optimizations. The library is now almost as fast as the original stackblur, and additionally it no longer violates borrowing rules and no longer requires nightly.

    • The internal Argb struct (which is used by methods like blur_argb and blur_srgb) has been changed to use an array instead of SIMD, due to portable_simd being an unstable feature that also generates very inefficient code.

      Additionally, it now uses unsigned (but explicitly wrapping) arithmetic for another small performance boost.

    • StackBlur will now only initialize itself if it receives at least one item from the contained iterator, for a small performance boost when the end has been reached.

    • Usage of the internal deque has been optimized slightly for another small performance boost.

    • StackBlur::next is now inlineable.

    • blur_horiz and blur_vert now use optimized iterators internally which are much faster than their old versions, especially blur_vert.

    The library is now approaching the speed of the original stackblur, and already outperforms it for large blur radii.

    Source code(tar.gz)
    Source code(zip)
Owner
LoganDark
Former CTO of Allotrope Labs
LoganDark
Toy language that output pseudocode, pascal and graphviz dot

pseudoc pseudoc is a project I made for school because we needed to write our programs in three different formats: Pascal, pseudocode (similar in stru

João Capucho 2 Apr 6, 2022
A tool to deserialize data from an input encoding, transform it and serialize it back into an output encoding.

dts A simple tool to deserialize data from an input encoding, transform it and serialize it back into an output encoding. Requires rust >= 1.56.0. Ins

null 11 Dec 14, 2022
Grimsby is an Erlang Port written in Rust that can close its standard input while retaining standard output (and error)

Grimsby An Erlang Port provides the basic mechanism for communication from Erlang with the external world. From the Ports and Port Drivers: Erlang Ref

Peter Morgan 5 May 29, 2023
Cogo is a high-performance library for programming stackful coroutines with which you can easily develop and maintain massive concurrent programs.

Cogo is a high-performance library for programming stackful coroutines with which you can easily develop and maintain massive concurrent programs.

co-rs 47 Nov 17, 2022
High-order Virtual Machine (HVM) is a pure functional compile target that is lazy, non-garbage-collected and massively parallel

High-order Virtual Machine (HVM) High-order Virtual Machine (HVM) is a pure functional compile target that is lazy, non-garbage-collected and massivel

null 5.5k Jan 2, 2023
Nyah is a programming language runtime built for high performance and comes with a scripting language.

?? Nyah ( Unfinished ) Nyah is a programming language runtime built for high performance and comes with a scripting language. ??️ Status Nyah is not c

Stacker 3 Mar 6, 2022
High Assurance Rust - A free book about developing secure and robust systems software.

High Assurance Rust - A free book about developing secure and robust systems software.

Tiemoko Ballo 1.1k Jan 9, 2023
🐱 A high-speed JIT programming language and its runtime, meow~

?? A high-speed JIT programming language and its runtime, meow~

EnabledFish 30 Dec 22, 2022
Support SIMD low-memory overhead and high-performance adaptive radix tree.

Artful Artful is an adaptive radix tree library for Rust. At a high-level, it's like a BTreeMap. It is based on the implementation of paper, see The A

future 3 Sep 7, 2022
An AI-native lightweight, reliable, and high performance open-source vector database.

What is OasysDB? OasysDB is a vector database that can be used to store and query high-dimensional vectors. Our goal is to make OasysDB fast and easy

Oasys 3 Dec 25, 2023
Gecko is a high-level, general-purpose programming language built on top of the LLVM project.

Gecko is a high-level, general-purpose programming language built on top of the LLVM project. Gecko Technology & principles Gecko is a general-purpose

Gecko 19 Oct 3, 2022
High concurrency, RealTime, In-memory storage inspired by erlang mnesia

DarkBird is a Document oriented, high concurrency in-memory Storage, also persist data to disk to avoid loss any data The darkbird provides the follow

DanyalMh 25 Dec 15, 2022
A type-safe, high speed programming language for scalable systems

A type-safe, high speed programming language for scalable systems! (featuring a cheesy logo!) note: the compiler is unfinished and probably buggy. if

Hail 0 Sep 14, 2022
Rust Imaging Library: A high-level Rust imaging crate.

ril Rust Imaging Library: A performant and high-level Rust imaging crate. Documentation • Crates.io • Discord What's this? This is a Rust crate design

Jay3332 18 Jan 5, 2023
Fast and simple datetime, date, time and duration parsing for rust.

speedate Fast and simple datetime, date, time and duration parsing for rust. speedate is a lax† RFC 3339 date and time parser, in other words, it pars

Samuel Colvin 43 Nov 25, 2022
fast rust implementation of online nonnegative matrix factorization as laid out in the paper "detect and track latent factors with online nonnegative matrix factorization"

ONMF status: early work in progress. still figuring this out. code still somewhat messy. api still in flux. fast rust implementation of online nonnega

null 2 Apr 10, 2020
A fast lean and clean modern constraint programming solver implementation (in rust)

MaxiCP-rs This project aims at implementing a fast, and clean constraint programming solver with a focus on correctness, simplicity, maintainability a

Xavier Gillard 5 Dec 10, 2022
Simple and fast proxy checker that include protocol validation;

Open Proxies ⭐️ Leave me a start please ⭐️ it will motivate me to continue maintaining and adding futures About | Technologies | Requirements | Starti

kmoz000 3 Nov 29, 2022
A backend framework for building fast and flexible APIs rapidly.

Andromeda Andromeda is a backend framework for Rust, to simplify the development of the kinds of basic API services that we developers have to build s

Framesurge 7 Dec 28, 2022