| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ccf7011 commit 97ba0be
43 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,129 @@ | |||
| 1 | + | ||
| 2 | + # Safety and unchecked code | ||
| 3 | + | ||
| 4 | + Cpp2 aims to be safe by default, usually entirely at compile time, and when needed at run time. | ||
| 5 | + | ||
| 6 | + When Cpp2 rejects unsafe code (e.g., signed/unsigned comparison) or ensuring safety can require run-time checks (e.g., subscripts bounds checks), you can opt out as needed in two ways: | ||
| 7 | + | ||
| 8 | + - at a specific place in your code, using `unchecked_*` functions (these are in namespace `cpp2::`, but can be used unqualified from Cpp2 code) | ||
| 9 | + - for a whole source file, using `-no-*-checks` switches | ||
| 10 | + | ||
| 11 | + Nearly always, you should opt out at a specific place in your code where you are confident the result is okay, and if there is a run-time check you have measured that the performance difference matters such as in a hot loop. | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + ### <a id="mixed-sign-comparison"></a> Integer mixed-sign `<`, `<=`, `>`, and `>=` (compile-time enforced) | ||
| 15 | + | ||
| 16 | + Comparing signed and unsigned integer values directly using `<`, `<=`, `>`, or `>=` can give wrong results, and so such comparisons are rejected at compile time. | ||
| 17 | + | ||
| 18 | + To disable this check at a specific place in your code, use the appropriate `unchecked_cmp_*` function instead of the operator notation: `unchecked_cmp_less`, `unchecked_cmp_less_eq`,`unchecked_cmp_greater`, or `unchecked_cmp_greater_eq`. | ||
| 19 | + | ||
| 20 | + For example: | ||
| 21 | + | ||
| 22 | + ``` cpp title="Integer comparisons" hl_lines="7" | ||
| 23 | + main: () = { | ||
| 24 | + x: i32 = 42; | ||
| 25 | + y: u32 = 43; | ||
| 26 | + | ||
| 27 | + if x < y { } // unsafe, therefore error by default | ||
| 28 | + | ||
| 29 | + if unchecked_cmp_less(x,y) { } // ok, explicit "trust me" opt-out | ||
| 30 | + } | ||
| 31 | + ``` | ||
| 32 | + | ||
| 33 | + To disable these checks for the entire source file, you can use cppfront's `-no-comparison-checks` switch: | ||
| 34 | + | ||
| 35 | + ``` bash title="Disable prevention of mixed-sign integer comparisons" hl_lines="3" | ||
| 36 | + cppfront myfile.cpp2 # mixed-sign int comparisons banned | ||
| 37 | + | ||
| 38 | + cppfront myfile.cpp2 -no-comparison-checks # mixed-sign int comparisons allowed | ||
| 39 | + ``` | ||
| 40 | + | ||
| 41 | + | ||
| 42 | + ### <a id="division-by-zero"></a> Division by zero (run-time checked) | ||
| 43 | + | ||
| 44 | + Dividing integers by zero is undefined behavior, and is rejected at run time by checking the denominator is nonzero. | ||
| 45 | + | ||
| 46 | + To disable this check at a specific place in your code, use `unchecked_div`. For example: | ||
| 47 | + | ||
| 48 | + ``` cpp title="Division by zero" hl_lines="7" | ||
| 49 | + main: () = { | ||
| 50 | + x := 42; | ||
| 51 | + y := 0; | ||
| 52 | + | ||
| 53 | + z := x/y; // unsafe, therefore run-time checked | ||
| 54 | + | ||
| 55 | + w := unchecked_div(x,y) // ok, explicit "trust me" opt-out | ||
| 56 | + } | ||
| 57 | + ``` | ||
| 58 | + | ||
| 59 | + To disable these checks for the entire source file, you can use cppfront's `-no-div-zero-checks` switch: | ||
| 60 | + | ||
| 61 | + ``` bash title="Disable prevention of division by zero" hl_lines="3" | ||
| 62 | + cppfront myfile.cpp2 # division by zero checked | ||
| 63 | + | ||
| 64 | + cppfront myfile.cpp2 -no-div-zero-checks # division by zero not checked | ||
| 65 | + ``` | ||
| 66 | + | ||
| 67 | + | ||
| 68 | + ### <a id="null-dereference"></a> Null dereference (run-time checked) | ||
| 69 | + | ||
| 70 | + Dereferencing a null pointer is undefined behavior, and is rejected at run time by checking the pointer is nonzero. | ||
| 71 | + | ||
| 72 | + To disable this check at a specific place in your code, use `unchecked_dereference`. For example: | ||
| 73 | + | ||
| 74 | + ``` cpp title="Null dereference" hl_lines="6" | ||
| 75 | + main: () = { | ||
| 76 | + p: *int = cpp1_func(); // could be initialized to null | ||
| 77 | + | ||
| 78 | + p* = 42; // unsafe, therefore run-time checked | ||
| 79 | + | ||
| 80 | + unchecked_dereference(p) = 42; // ok, explicit "trust me" opt-out | ||
| 81 | + } | ||
| 82 | + ``` | ||
| 83 | + | ||
| 84 | + To disable these checks for the entire source file, you can use cppfront's `-no-null-checks` switch: | ||
| 85 | + | ||
| 86 | + ``` bash title="Disable prevention of null deference" hl_lines="3" | ||
| 87 | + cppfront myfile.cpp2 # null dereferences checked | ||
| 88 | + | ||
| 89 | + cppfront myfile.cpp2 -no-null-checks # null dereferences not checked | ||
| 90 | + ``` | ||
| 91 | + | ||
| 92 | + | ||
| 93 | + ### <a id="subscript-bounds"></a> Subscript bounds (run-time checked) | ||
| 94 | + | ||
| 95 | + Accessing an out of bounds subscript is undefined behavior, and is rejected at run time by checking the subscript is nonzero. For an expression `a[b]` where | ||
| 96 | + | ||
| 97 | + - `a` is contiguous and supports `std::size(a)`, and | ||
| 98 | + - `b` is an integral value | ||
| 99 | + | ||
| 100 | + the cppfront compiler injects a check that **`0 <= b < std::size(a)`** before the call to `a[b]`. | ||
| 101 | + | ||
| 102 | + To disable this check at a specific place in your code, use `unchecked_subscript`. For example: | ||
| 103 | + | ||
| 104 | + ``` cpp title="Subscript bounds" hl_lines="12 13" | ||
| 105 | + main: () = { | ||
| 106 | + v: std::vector = ( 1, 2, 3, 4, 5 ); | ||
| 107 | + s: std::span = v; | ||
| 108 | + | ||
| 109 | + idx := calc_index(s); | ||
| 110 | + | ||
| 111 | + v[idx] += 42; // unsafe, therefore run-time checked | ||
| 112 | + s[idx] += 84; // unsafe, therefore run-time checked | ||
| 113 | + | ||
| 114 | + // manually hoist the check and do it myself | ||
| 115 | + if (0 ..< v.size()).contains(idx) { | ||
| 116 | + unchecked_subscript(v,idx) += 42; // ok, explicit "trust me" opt-out | ||
| 117 | + unchecked_subscript(s,idx) += 84; // ok, explicit "trust me" opt-out | ||
| 118 | + } | ||
| 119 | + } | ||
| 120 | + ``` | ||
| 121 | + | ||
| 122 | + To disable these checks for the entire source file, you can use cppfront's `-no-subscript-checks` switch: | ||
| 123 | + | ||
| 124 | + ``` bash title="Disable prevention of out-of-bounds subscripts" hl_lines="3" | ||
| 125 | + cppfront myfile.cpp2 # subscript bounds checked | ||
| 126 | + | ||
| 127 | + cppfront myfile.cpp2 -no-subscript-checks # subscript bounds not checked | ||
| 128 | + ``` | ||
| 129 | + | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2601,7 +2601,7 @@ range( | |||
| 2601 | 2601 | ) -> range<std::common_type_t<T, U>>; | |
| 2602 | 2602 | ||
| 2603 | 2603 | template<typename T> | |
| 2604 | - constexpr auto contains(range<T> const& r, T const& t) | ||
| 2604 | + constexpr auto contains(range<T> const& r, auto const& t) | ||
| 2605 | 2605 | -> bool | |
| 2606 | 2606 | { | |
| 2607 | 2607 | if (r.empty()) { | |
@@ -2711,6 +2711,64 @@ inline auto fopen( const char* filename, const char* mode ) { | |||
| 2711 | 2711 | ||
| 2712 | 2712 | ||
| 2713 | 2713 | ||
| 2714 | + | ||
| 2715 | + //----------------------------------------------------------------------- | ||
| 2716 | + // | ||
| 2717 | + // "Unchecked" opt-outs for safety checks | ||
| 2718 | + // | ||
| 2719 | + //----------------------------------------------------------------------- | ||
| 2720 | + // | ||
| 2721 | + | ||
| 2722 | + CPP2_FORCE_INLINE constexpr auto unchecked_cmp_less(auto&& t, auto&& u) | ||
| 2723 | + -> decltype(auto) | ||
| 2724 | + requires requires {CPP2_FORWARD(t) < CPP2_FORWARD(u);} | ||
| 2725 | + { | ||
| 2726 | + return CPP2_FORWARD(t) < CPP2_FORWARD(u); | ||
| 2727 | + } | ||
| 2728 | + | ||
| 2729 | + CPP2_FORCE_INLINE constexpr auto unchecked_cmp_less_eq(auto&& t, auto&& u) | ||
| 2730 | + -> decltype(auto) | ||
| 2731 | + requires requires {CPP2_FORWARD(t) <= CPP2_FORWARD(u);} | ||
| 2732 | + { | ||
| 2733 | + return CPP2_FORWARD(t) <= CPP2_FORWARD(u); | ||
| 2734 | + } | ||
| 2735 | + | ||
| 2736 | + CPP2_FORCE_INLINE constexpr auto unchecked_cmp_greater(auto&& t, auto&& u) | ||
| 2737 | + -> decltype(auto) | ||
| 2738 | + requires requires {CPP2_FORWARD(t) > CPP2_FORWARD(u);} | ||
| 2739 | + { | ||
| 2740 | + return CPP2_FORWARD(t) > CPP2_FORWARD(u); | ||
| 2741 | + } | ||
| 2742 | + | ||
| 2743 | + CPP2_FORCE_INLINE constexpr auto unchecked_cmp_greater_eq(auto&& t, auto&& u) | ||
| 2744 | + -> decltype(auto) | ||
| 2745 | + requires requires {CPP2_FORWARD(t) >= CPP2_FORWARD(u);} | ||
| 2746 | + { | ||
| 2747 | + return CPP2_FORWARD(t) >= CPP2_FORWARD(u); | ||
| 2748 | + } | ||
| 2749 | + | ||
| 2750 | + CPP2_FORCE_INLINE constexpr auto unchecked_div(auto&& t, auto&& u) | ||
| 2751 | + -> decltype(auto) | ||
| 2752 | + requires requires {CPP2_FORWARD(t) / CPP2_FORWARD(u);} | ||
| 2753 | + { | ||
| 2754 | + return CPP2_FORWARD(t) / CPP2_FORWARD(u); | ||
| 2755 | + } | ||
| 2756 | + | ||
| 2757 | + CPP2_FORCE_INLINE constexpr auto unchecked_dereference(auto&& p) | ||
| 2758 | + -> decltype(auto) | ||
| 2759 | + requires requires {*CPP2_FORWARD(p);} | ||
| 2760 | + { | ||
| 2761 | + return *CPP2_FORWARD(p); | ||
| 2762 | + } | ||
| 2763 | + | ||
| 2764 | + CPP2_FORCE_INLINE constexpr auto unchecked_subscript(auto&& a, auto&& b) | ||
| 2765 | + -> decltype(auto) | ||
| 2766 | + requires requires {CPP2_FORWARD(a)[b];} | ||
| 2767 | + { | ||
| 2768 | + return CPP2_FORWARD(a)[b]; | ||
| 2769 | + } | ||
| 2770 | + | ||
| 2771 | + | ||
| 2714 | 2772 | namespace impl { | |
| 2715 | 2773 | ||
| 2716 | 2774 | //----------------------------------------------------------------------- | |
@@ -2729,7 +2787,8 @@ CPP2_FORCE_INLINE constexpr auto cmp_mixed_signedness_check() -> void | |||
| 2729 | 2787 | { | |
| 2730 | 2788 | static_assert( | |
| 2731 | 2789 | program_violates_type_safety_guarantee<T, U>, | |
| 2732 | - "comparing bool values using < <= >= > is unsafe and not allowed - are you missing parentheses?"); | ||
| 2790 | + "comparing bool values using < <= >= > is unsafe and not allowed - are you missing parentheses?" | ||
| 2791 | + ); | ||
| 2733 | 2792 | } | |
| 2734 | 2793 | else if constexpr ( | |
| 2735 | 2794 | std::is_integral_v<T> && | |
@@ -2745,20 +2804,22 @@ CPP2_FORCE_INLINE constexpr auto cmp_mixed_signedness_check() -> void | |||
| 2745 | 2804 | // static_assert to reject the comparison is the right way to go. | |
| 2746 | 2805 | static_assert( | |
| 2747 | 2806 | program_violates_type_safety_guarantee<T, U>, | |
| 2748 | - "mixed signed/unsigned comparison is unsafe - prefer using .ssize() instead of .size(), consider using std::cmp_less instead, or consider explicitly casting one of the values to change signedness by using 'as' or 'cpp2::unchecked_narrow'" | ||
| 2807 | + "mixed signed/unsigned comparison is unsafe - prefer using .ssize() instead of .size(), consider using std::cmp_less or similar instead, or consider explicitly casting one of the values to change signedness by using 'as' or 'cpp2::unchecked_narrow'" | ||
| 2749 | 2808 | ); | |
| 2750 | 2809 | } | |
| 2751 | 2810 | } | |
| 2752 | 2811 | ||
| 2753 | 2812 | ||
| 2754 | - CPP2_FORCE_INLINE constexpr auto cmp_less(auto&& t, auto&& u) -> decltype(auto) | ||
| 2813 | + CPP2_FORCE_INLINE constexpr auto cmp_less(auto&& t, auto&& u) | ||
| 2814 | + -> decltype(auto) | ||
| 2755 | 2815 | requires requires {CPP2_FORWARD(t) < CPP2_FORWARD(u);} | |
| 2756 | 2816 | { | |
| 2757 | 2817 | cmp_mixed_signedness_check<CPP2_TYPEOF(t), CPP2_TYPEOF(u)>(); | |
| 2758 | 2818 | return CPP2_FORWARD(t) < CPP2_FORWARD(u); | |
| 2759 | 2819 | } | |
| 2760 | 2820 | ||
| 2761 | - CPP2_FORCE_INLINE constexpr auto cmp_less(auto&& t, auto&& u) -> decltype(auto) | ||
| 2821 | + CPP2_FORCE_INLINE constexpr auto cmp_less(auto&& t, auto&& u) | ||
| 2822 | + -> decltype(auto) | ||
| 2762 | 2823 | { | |
| 2763 | 2824 | static_assert( | |
| 2764 | 2825 | program_violates_type_safety_guarantee<decltype(t), decltype(u)>, | |
@@ -2768,14 +2829,16 @@ CPP2_FORCE_INLINE constexpr auto cmp_less(auto&& t, auto&& u) -> decltype(auto) | |||
| 2768 | 2829 | } | |
| 2769 | 2830 | ||
| 2770 | 2831 | ||
| 2771 | - CPP2_FORCE_INLINE constexpr auto cmp_less_eq(auto&& t, auto&& u) -> decltype(auto) | ||
| 2832 | + CPP2_FORCE_INLINE constexpr auto cmp_less_eq(auto&& t, auto&& u) | ||
| 2833 | + -> decltype(auto) | ||
| 2772 | 2834 | requires requires {CPP2_FORWARD(t) <= CPP2_FORWARD(u);} | |
| 2773 | 2835 | { | |
| 2774 | 2836 | cmp_mixed_signedness_check<CPP2_TYPEOF(t), CPP2_TYPEOF(u)>(); | |
| 2775 | 2837 | return CPP2_FORWARD(t) <= CPP2_FORWARD(u); | |
| 2776 | 2838 | } | |
| 2777 | 2839 | ||
| 2778 | - CPP2_FORCE_INLINE constexpr auto cmp_less_eq(auto&& t, auto&& u) -> decltype(auto) | ||
| 2840 | + CPP2_FORCE_INLINE constexpr auto cmp_less_eq(auto&& t, auto&& u) | ||
| 2841 | + -> decltype(auto) | ||
| 2779 | 2842 | { | |
| 2780 | 2843 | static_assert( | |
| 2781 | 2844 | program_violates_type_safety_guarantee<decltype(t), decltype(u)>, | |
@@ -2785,14 +2848,16 @@ CPP2_FORCE_INLINE constexpr auto cmp_less_eq(auto&& t, auto&& u) -> decltype(aut | |||
| 2785 | 2848 | } | |
| 2786 | 2849 | ||
| 2787 | 2850 | ||
| 2788 | - CPP2_FORCE_INLINE constexpr auto cmp_greater(auto&& t, auto&& u) -> decltype(auto) | ||
| 2851 | + CPP2_FORCE_INLINE constexpr auto cmp_greater(auto&& t, auto&& u) | ||
| 2852 | + -> decltype(auto) | ||
| 2789 | 2853 | requires requires {CPP2_FORWARD(t) > CPP2_FORWARD(u);} | |
| 2790 | 2854 | { | |
| 2791 | 2855 | cmp_mixed_signedness_check<CPP2_TYPEOF(t), CPP2_TYPEOF(u)>(); | |
| 2792 | 2856 | return CPP2_FORWARD(t) > CPP2_FORWARD(u); | |
| 2793 | 2857 | } | |
| 2794 | 2858 | ||
| 2795 | - CPP2_FORCE_INLINE constexpr auto cmp_greater(auto&& t, auto&& u) -> decltype(auto) | ||
| 2859 | + CPP2_FORCE_INLINE constexpr auto cmp_greater(auto&& t, auto&& u) | ||
| 2860 | + -> decltype(auto) | ||
| 2796 | 2861 | { | |
| 2797 | 2862 | static_assert( | |
| 2798 | 2863 | program_violates_type_safety_guarantee<decltype(t), decltype(u)>, | |
@@ -2802,14 +2867,16 @@ CPP2_FORCE_INLINE constexpr auto cmp_greater(auto&& t, auto&& u) -> decltype(aut | |||
| 2802 | 2867 | } | |
| 2803 | 2868 | ||
| 2804 | 2869 | ||
| 2805 | - CPP2_FORCE_INLINE constexpr auto cmp_greater_eq(auto&& t, auto&& u) -> decltype(auto) | ||
| 2870 | + CPP2_FORCE_INLINE constexpr auto cmp_greater_eq(auto&& t, auto&& u) | ||
| 2871 | + -> decltype(auto) | ||
| 2806 | 2872 | requires requires {CPP2_FORWARD(t) >= CPP2_FORWARD(u);} | |
| 2807 | 2873 | { | |
| 2808 | 2874 | cmp_mixed_signedness_check<CPP2_TYPEOF(t), CPP2_TYPEOF(u)>(); | |
| 2809 | 2875 | return CPP2_FORWARD(t) >= CPP2_FORWARD(u); | |
| 2810 | 2876 | } | |
| 2811 | 2877 | ||
| 2812 | - CPP2_FORCE_INLINE constexpr auto cmp_greater_eq(auto&& t, auto&& u) -> decltype(auto) | ||
| 2878 | + CPP2_FORCE_INLINE constexpr auto cmp_greater_eq(auto&& t, auto&& u) | ||
| 2879 | + -> decltype(auto) | ||
| 2813 | 2880 | { | |
| 2814 | 2881 | static_assert( | |
| 2815 | 2882 | program_violates_type_safety_guarantee<decltype(t), decltype(u)>, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -64,6 +64,7 @@ nav: | |||
| 64 | 64 | - 'Types and inheritance': cpp2/types.md | |
| 65 | 65 | - 'Metafunctions and reflection': cpp2/metafunctions.md | |
| 66 | 66 | - 'Namespaces': cpp2/namespaces.md | |
| 67 | + - 'Safety and "unchecked"': cpp2/safety.md | ||
| 67 | 68 | # - 'Modules': cpp2/modules.md | |
| 68 | 69 | - 'Cppfront reference': | |
| 69 | 70 | - 'Using Cpp1 (today''s syntax) and Cpp2 in the same source file': cppfront/mixed.md | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,7 +7,7 @@ main: () -> int = { | |||
| 7 | 7 | std::cout << i << "\n"; | |
| 8 | 8 | } | |
| 9 | 9 | ||
| 10 | - add_42_to_subrange: (inout rng:_, start:int, end:int) | ||
| 10 | + add_42_to_subrange: (inout rng, start:int, end:int) | ||
| 11 | 11 | = { | |
| 12 | 12 | assert<bounds_safety>( 0 <= start ); | |
| 13 | 13 | assert<bounds_safety>( end <= rng.ssize() ); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,7 +6,7 @@ main: () -> int = { | |||
| 6 | 6 | print_subrange(v, 1, 13); | |
| 7 | 7 | } | |
| 8 | 8 | ||
| 9 | - print_subrange: (rng:_, start:int, end:int) = { | ||
| 9 | + print_subrange: (rng, start:int, end:int) = { | ||
| 10 | 10 | assert<bounds_safety>( 0 <= start ); | |
| 11 | 11 | assert<bounds_safety>( end <= rng.ssize() ); | |
| 12 | 12 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,7 +6,7 @@ namespace my { | |||
| 6 | 6 | using u16 = float; | |
| 7 | 7 | } | |
| 8 | 8 | ||
| 9 | - test: (x:_) = { | ||
| 9 | + test: (x) = { | ||
| 10 | 10 | std::cout | |
| 11 | 11 | << std::is_floating_point_v<CPP2_TYPEOF(x)> as std::string | |
| 12 | 12 | << "\n"; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -32,7 +32,7 @@ main: () -> int = | |||
| 32 | 32 | ||
| 33 | 33 | // Print! A one-expression function body... | |
| 34 | 34 | // | |
| 35 | - print_decorated: (x:_) = std::cout << ">> [" << x << "]\n"; | ||
| 35 | + print_decorated: (x) = std::cout << ">> [" << x << "]\n"; | ||
| 36 | 36 | ||
| 37 | 37 | // Flip a coin! Exercise <mutex> <cstdlib> <ctime> and 'as'... | |
| 38 | 38 | // | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -32,7 +32,7 @@ main: () -> int = | |||
| 32 | 32 | ||
| 33 | 33 | // Print! A one-expression function body... | |
| 34 | 34 | // | |
| 35 | - print_decorated: (x:_) = std::cout << ">> [" << x << "]\n"; | ||
| 35 | + print_decorated: (x) = std::cout << ">> [" << x << "]\n"; | ||
| 36 | 36 | ||
| 37 | 37 | // Flip a coin! Exercise <mutex> <cstdlib> <ctime> and 'as'... | |
| 38 | 38 | // | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,7 +25,7 @@ fill: ( | |||
| 25 | 25 | x = value.substr(0, count); | |
| 26 | 26 | } | |
| 27 | 27 | ||
| 28 | - print_decorated: (x:_) = { | ||
| 28 | + print_decorated: (x) = { | ||
| 29 | 29 | std::cout << ">> [" << x << "]\n"; | |
| 30 | 30 | } | |
| 31 | 31 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,7 +23,7 @@ fill: ( | |||
| 23 | 23 | x = value.substr(0, count); | |
| 24 | 24 | } | |
| 25 | 25 | ||
| 26 | - print_decorated: (x:_) = { | ||
| 26 | + print_decorated: (x) = { | ||
| 27 | 27 | std::cout << ">> [" << x << "]\n"; | |
| 28 | 28 | } | |
| 29 | 29 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments