Changeset c866a83 in mainline
- Timestamp:
- 2018-07-05T21:41:23Z (7 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 614b07e
- Parents:
- daef596
- git-author:
- Dzejrou <dzejrou@…> (2018-05-05 16:42:49)
- git-committer:
- Dzejrou <dzejrou@…> (2018-07-05 21:41:23)
- Location:
- uspace/lib/cpp/include
- Files:
-
- 9 added
- 7 edited
- 2 moved
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/cpp/include/functional
rdaef596 rc866a83 28 28 29 29 #include <impl/functional.hpp> 30 #include <internal/functional/bind.hpp> 31 #include <internal/functional/function.hpp> 32 #include <internal/functional/invoke.hpp> 33 #include <internal/functional/reference_wrapper.hpp> -
uspace/lib/cpp/include/impl/functional.hpp
rdaef596 rc866a83 30 30 #define LIBCPP_FUNCTIONAL 31 31 32 #include <internal/functional/invoke.hpp> 32 33 #include <limits> 33 34 #include <memory> 34 #include <typeinfo>35 35 #include <type_traits> 36 36 #include <utility> … … 38 38 namespace std 39 39 { 40 namespace aux41 {42 /**43 * 20.9.2, requirements:44 */45 template<class R, class T, class T1, class... Ts>46 decltype(auto) invoke(R T::* f, T1&& t1, Ts&&... args)47 {48 if constexpr (is_member_function_pointer_v<decltype(f)>)49 {50 if constexpr (is_base_of_v<T, remove_reference_t<T1>>)51 // (1.1)52 return (t1.*f)(forward<Ts>(args)...);53 else54 // (1.2)55 return ((*t1).*f)(forward<Ts>(args)...);56 }57 else if constexpr (is_member_object_pointer_v<decltype(f)> && sizeof...(args) == 0)58 {59 /**60 * Note: Standard requires to N be equal to 1, but we take t1 directly61 * so we need sizeof...(args) to be 0.62 */63 if constexpr (is_base_of_v<T, remove_reference_t<T1>>)64 // (1.3)65 return t1.*f;66 else67 // (1.4)68 return (*t1).*f;69 }70 71 /**72 * Note: If this condition holds this will not be reachable,73 * but a new addition to the standard (17.7 point (8.1))74 * prohibits us from simply using false as the condition here,75 * so we use this because we know it is false here.76 */77 static_assert(is_member_function_pointer_v<decltype(f)>, "invalid invoke");78 }79 80 template<class F, class... Args>81 decltype(auto) invoke(F&& f, Args&&... args)82 {83 // (1.5)84 return f(forward<Args>(args)...);85 }86 }87 88 40 /** 89 41 * 20.9.3, invoke: … … 91 43 92 44 template<class F, class... Args> 93 result_of_t<F&&(Args&&...)>invoke(F&& f, Args&&... args)45 decltype(auto) invoke(F&& f, Args&&... args) 94 46 { 95 47 return aux::invoke(forward<F>(f)(forward<Args>(args)...)); 96 }97 98 /**99 * 20.9.4, reference_wrapper:100 */101 102 template<class T>103 class reference_wrapper104 {105 public:106 using type = T;107 // TODO: conditional typedefs108 109 reference_wrapper(type& val) noexcept110 : data_{&val}111 { /* DUMMY BODY */ }112 113 reference_wrapper(type&&) = delete;114 115 reference_wrapper(const reference_wrapper& other) noexcept116 : data_{other.data_}117 { /* DUMMY BODY */ }118 119 reference_wrapper& operator=(const reference_wrapper& other) noexcept120 {121 data_ = other.data_;122 123 return *this;124 }125 126 operator type&() const noexcept127 {128 return *data_;129 }130 131 type& get() const noexcept132 {133 return *data_;134 }135 136 template<class... Args>137 result_of_t<type&(Args&&...)> operator()(Args&&... args) const138 {139 return invoke(*data_, std::forward<Args>(args)...);140 }141 142 private:143 type* data_;144 };145 146 template<class T>147 reference_wrapper<T> ref(T& t) noexcept148 {149 return reference_wrapper<T>{t};150 }151 152 template<class T>153 reference_wrapper<const T> cref(const T& t) noexcept154 {155 return reference_wrapper<const T>{t};156 }157 158 template<class T>159 void ref(const T&&) = delete;160 161 template<class T>162 void cref(const T&&) = delete;163 164 template<class T>165 reference_wrapper<T> ref(reference_wrapper<T> t) noexcept166 {167 return ref(t.get());168 }169 170 template<class T>171 reference_wrapper<const T> cref(reference_wrapper<T> t) noexcept172 {173 return cref(t.get());174 48 } 175 49 … … 756 630 template<class Predicate> 757 631 constexpr binary_negate<Predicate> not2(const Predicate& pred); 758 759 /**760 * 20.9.12, polymorphic function adaptors:761 */762 763 namespace aux764 {765 // TODO: fix this766 /* template<class, class T, class... Args> */767 /* struct is_callable_impl: false_type */768 /* { /1* DUMMY BODY *1/ }; */769 770 /* template<class, class R, class... Args> */771 /* struct is_callable_impl< */772 /* void_t<decltype(aux::invoke(declval<R(Args...)>(), declval<Args>()..., R))>, */773 /* R, Args... */774 /* > : true_type */775 /* { /1* DUMMY BODY *1/ }; */776 777 /* template<class T> */778 /* struct is_callable: is_callable_impl<void_t<>, T> */779 /* { /1* DUMMY BODY *1/ }; */780 781 template<class Callable, class R, class... Args>782 R invoke_callable(Callable* clbl, Args&&... args)783 {784 return (*clbl)(forward<Args>(args)...);785 }786 787 template<class Callable>788 void copy_callable(Callable* to, Callable* from)789 {790 new(to) Callable{*from};791 }792 793 template<class Callable>794 void destroy_callable(Callable* clbl)795 {796 if (clbl)797 clbl->~Callable();798 }799 }800 801 // TODO: implement802 class bad_function_call;803 804 template<class>805 class function; // undefined806 807 /**808 * Note: Ideally, this implementation wouldn't809 * copy the target if it was a pointer to810 * a function, but for the simplicity of the811 * implementation, we do copy even in that812 * case for now. It would be a nice optimization813 * if this was changed in the future.814 */815 template<class R, class... Args>816 class function<R(Args...)>817 {818 public:819 using result_type = R;820 // TODO: conditional typedefs821 822 /**823 * 20.9.12.2.1, construct/copy/destroy:824 */825 826 function() noexcept827 : callable_{}, callable_size_{}, call_{},828 copy_{}, dest_{}829 { /* DUMMY BODY */ }830 831 function(nullptr_t) noexcept832 : function{}833 { /* DUMMY BODY */ }834 835 function(const function& other)836 : callable_{}, callable_size_{other.callable_size_},837 call_{other.call_}, copy_{other.copy_}, dest_{other.dest_}838 {839 callable_ = new uint8_t[callable_size_];840 (*copy_)(callable_, other.callable_);841 }842 843 function(function&& other)844 : callable_{other.callable_}, callable_size_{other.callable_size_},845 call_{other.call_}, copy_{other.copy_}, dest_{other.dest_}846 {847 other.callable_ = nullptr;848 other.callable_size_ = size_t{};849 other.call_ = nullptr;850 other.copy_ = nullptr;851 other.dest_ = nullptr;852 }853 854 // TODO: shall not participate in overloading unless aux::is_callable<F>855 template<class F>856 function(F f)857 : callable_{}, callable_size_{sizeof(F)},858 call_{(call_t)aux::invoke_callable<F, R, Args...>},859 copy_{(copy_t)aux::copy_callable<F>},860 dest_{(dest_t)aux::destroy_callable<F>}861 {862 callable_ = new uint8_t[callable_size_];863 (*copy_)(callable_, (uint8_t*)&f);864 }865 866 /**867 * Note: For the moment we're ignoring the allocator868 * for simplicity of the implementation.869 */870 871 template<class A>872 function(allocator_arg_t, const A& a) noexcept873 : function{}874 { /* DUMMY BODY */ }875 876 template<class A>877 function(allocator_arg_t, const A& a, nullptr_t) noexcept878 : function{}879 { /* DUMMY BODY */ }880 881 template<class A>882 function(allocator_arg_t, const A& a, const function& other)883 : function{other}884 { /* DUMMY BODY */ }885 886 template<class A>887 function(allocator_arg_t, const A& a, function&& other)888 : function{move(other)}889 { /* DUMMY BODY */ }890 891 // TODO: shall not participate in overloading unless aux::is_callable<F>892 template<class F, class A>893 function(allocator_arg_t, const A& a, F f)894 : function{f}895 { /* DUMMY BODY */ }896 897 function& operator=(const function& rhs)898 {899 function{rhs}.swap(*this);900 901 return *this;902 }903 904 /**905 * Note: We have to copy call_, copy_906 * and dest_ because they can be templated907 * by a type F we don't know.908 */909 function& operator=(function&& rhs)910 {911 clear_();912 913 callable_ = rhs.callable_;914 callable_size_ = rhs.callable_size_;915 call_ = rhs.call_;916 copy_ = rhs.copy_;917 dest_ = rhs.dest_;918 919 rhs.callable_ = nullptr;920 rhs.callable_size_ = size_t{};921 rhs.call_ = nullptr;922 rhs.copy_ = nullptr;923 rhs.dest_ = nullptr;924 925 return *this;926 }927 928 function& operator=(nullptr_t) noexcept929 {930 clear_();931 932 return *this;933 }934 935 // TODO: shall not participate in overloading unless aux::is_callable<F>936 template<class F>937 function& operator=(F&& f)938 {939 callable_size_ = sizeof(F);940 callable_ = new uint8_t[callable_size_];941 call_ = aux::invoke_callable<F, R, Args...>;942 copy_ = aux::copy_callable<F>;943 dest_ = aux::destroy_callable<F>;944 945 (*copy_)(callable_, (uint8_t*)&f);946 }947 948 template<class F>949 function& operator=(reference_wrapper<F> ref) noexcept950 {951 return (*this) = ref.get();952 }953 954 ~function()955 {956 if (callable_)957 {958 (*dest_)(callable_);959 delete[] callable_;960 }961 }962 963 /**964 * 20.9.12.2.2, function modifiers:965 */966 967 void swap(function& other) noexcept968 {969 std::swap(callable_, other.callable_);970 std::swap(callable_size_, other.callable_size_);971 std::swap(call_, other.call_);972 std::swap(copy_, other.copy_);973 std::swap(dest_, other.dest_);974 }975 976 template<class F, class A>977 void assign(F&& f, const A& a)978 {979 function{allocator_arg, a, forward<F>(f)}.swap(*this);980 }981 982 /**983 * 20.9.12.2.3, function capacity:984 */985 986 explicit operator bool() const noexcept987 {988 return callable_ != nullptr;989 }990 991 /**992 * 20.9.12.2.4, function invocation:993 */994 995 result_type operator()(Args... args) const996 {997 // TODO: throw bad_function_call if !callable_ || !call_998 if constexpr (is_same_v<R, void>)999 (*call_)(callable_, forward<Args>(args)...);1000 else1001 return (*call_)(callable_, forward<Args>(args)...);1002 }1003 1004 /**1005 * 20.9.12.2.5, function target access:1006 */1007 1008 const type_info& target_type() const noexcept1009 {1010 return typeid(*callable_);1011 }1012 1013 template<class T>1014 T* target() noexcept1015 {1016 if (target_type() == typeid(T))1017 return (T*)callable_;1018 else1019 return nullptr;1020 }1021 1022 template<class T>1023 const T* target() const noexcept1024 {1025 if (target_type() == typeid(T))1026 return (T*)callable_;1027 else1028 return nullptr;1029 }1030 1031 private:1032 using call_t = R(*)(uint8_t*, Args&&...);1033 using copy_t = void (*)(uint8_t*, uint8_t*);1034 using dest_t = void (*)(uint8_t*);1035 1036 uint8_t* callable_;1037 size_t callable_size_;1038 call_t call_;1039 copy_t copy_;1040 dest_t dest_;1041 1042 void clear_()1043 {1044 if (callable_)1045 {1046 (*dest_)(callable_);1047 delete[] callable_;1048 callable_ = nullptr;1049 }1050 }1051 };1052 1053 /**1054 * 20.9.12.2.6, null pointer comparisons:1055 */1056 1057 template<class R, class... Args>1058 bool operator==(const function<R(Args...)>& f, nullptr_t) noexcept1059 {1060 return !f;1061 }1062 1063 template<class R, class... Args>1064 bool operator==(nullptr_t, const function<R(Args...)>& f) noexcept1065 {1066 return !f;1067 }1068 1069 template<class R, class... Args>1070 bool operator!=(const function<R(Args...)>& f, nullptr_t) noexcept1071 {1072 return (bool)f;1073 }1074 1075 template<class R, class... Args>1076 bool operator!=(nullptr_t, const function<R(Args...)>& f) noexcept1077 {1078 return (bool)f;1079 }1080 1081 /**1082 * 20.9.12.2.7, specialized algorithms:1083 */1084 1085 template<class R, class... Args>1086 void swap(function<R(Args...)>& f1, function<R(Args...)>& f2)1087 {1088 f1.swap(f2);1089 }1090 1091 template<class R, class... Args, class Alloc>1092 struct uses_allocator<function<R(Args...)>, Alloc>1093 : true_type1094 { /* DUMMY BODY */ };1095 1096 /**1097 * 20.9.10, bind:1098 */1099 1100 namespace aux1101 {1102 template<int N>1103 struct placeholder_t1104 {1105 constexpr placeholder_t() = default;1106 constexpr placeholder_t(const placeholder_t&) = default;1107 constexpr placeholder_t(placeholder_t&&) = default;1108 };1109 }1110 1111 template<class T>1112 struct is_placeholder: integral_constant<int, 0>1113 { /* DUMMY BODY */ };1114 1115 template<int N> // Note: const because they are all constexpr.1116 struct is_placeholder<const aux::placeholder_t<N>>1117 : integral_constant<int, N>1118 { /* DUMMY BODY */ };1119 1120 template<class T>1121 inline constexpr int is_placeholder_v = is_placeholder<T>::value;1122 1123 namespace aux1124 {1125 /**1126 * Note: Our internal bind return type has an extra type1127 * template parameter and an extra bool template parameter.1128 * We use this for the special version of bind that has1129 * the return type to have a result_type typedef1130 * (i.e. when the bool is true, the extra type parameter1131 * is typedefed as result_type - see the structure below).1132 * This is just a simplification of the implementation1133 * so that we don't need to have two return types for1134 * the two bind functions, because unlike function or1135 * mem_fn, we know exactly when to make the typedef.1136 */1137 1138 template<class, bool = false>1139 struct bind_conditional_result_type1140 { /* DUMMY BODY */ };1141 1142 template<class R>1143 struct bind_conditional_result_type<R, true>1144 {1145 using result_type = R;1146 };1147 1148 template<class, bool, class, class...>1149 class bind_t;1150 1151 /**1152 * Filter class that uses its overloaded operator[]1153 * to filter our placeholders, reference_wrappers and bind1154 * subexpressions and replace them with the correct1155 * arguments (extracts references, calls the subexpressions etc).1156 */1157 template<class... Args>1158 class bind_arg_filter1159 {1160 public:1161 bind_arg_filter(Args&&... args)1162 : args_{forward<Args>(args)...}1163 { /* DUMMY BODY */ }1164 1165 template<class T>1166 constexpr decltype(auto) operator[](T&& t)1167 {1168 return forward<T>(t);1169 }1170 1171 template<int N>1172 constexpr decltype(auto) operator[](const placeholder_t<N>)1173 { // Since placeholders are constexpr, this is the best match for them.1174 /**1175 * Come on, it's int! Why not use -1 as not placeholder1176 * and start them at 0? -.-1177 */1178 return get<N - 1>(args_);1179 }1180 1181 template<class T>1182 constexpr T& operator[](reference_wrapper<T> ref)1183 {1184 return ref.get();1185 }1186 1187 template<class R, bool B, class F, class... BindArgs>1188 constexpr decltype(auto) operator[](const bind_t<R, B, F, BindArgs...> b)1189 {1190 return b; // TODO: bind subexpressions1191 }1192 1193 1194 private:1195 tuple<Args...> args_;1196 };1197 1198 template<class R, bool HasResultType, class F, class... Args>1199 class bind_t: public bind_conditional_result_type<R, HasResultType>1200 {1201 public:1202 template<class G, class... BoundArgs>1203 constexpr bind_t(G&& g, BoundArgs&&... args)1204 : func_{forward<F>(g)},1205 bound_args_{forward<Args>(args)...}1206 { /* DUMMY BODY */ }1207 1208 constexpr bind_t(const bind_t& other) = default;1209 constexpr bind_t(bind_t&& other) = default;1210 1211 template<class... ActualArgs>1212 constexpr decltype(auto) operator()(ActualArgs&&... args)1213 {1214 return invoke_(1215 make_index_sequence<sizeof...(Args)>{},1216 forward<ActualArgs>(args)...1217 );1218 }1219 1220 private:1221 function<decay_t<F>> func_;1222 tuple<decay_t<Args>...> bound_args_;1223 1224 template<size_t... Is, class... ActualArgs>1225 constexpr decltype(auto) invoke_(1226 index_sequence<Is...>, ActualArgs&&... args1227 )1228 {1229 /**1230 * The expression filter[bound_args_[bind_arg_index<Is>()]]...1231 * here expands bind_arg_index to 0, 1, ... sizeof...(ActualArgs) - 11232 * and then passes this variadic list of indices to the bound_args_1233 * tuple which extracts the bound args from it.1234 * Our filter will then have its operator[] called on each of them1235 * and filter out the placeholders, reference_wrappers etc and changes1236 * them to the actual arguments.1237 */1238 bind_arg_filter<ActualArgs...> filter{forward<ActualArgs>(args)...};1239 1240 return invoke(1241 func_,1242 filter[get<Is>(bound_args_)]...1243 );1244 }1245 };1246 }1247 1248 template<class T>1249 struct is_bind_expression: false_type1250 { /* DUMMY BODY */ };1251 1252 template<class R, bool B, class F, class... Args>1253 struct is_bind_expression<aux::bind_t<R, B, F, Args...>>1254 : true_type1255 { /* DUMMY BODY */ };1256 1257 template<class F, class... Args>1258 aux::bind_t<void, false, F, Args...> bind(F&& f, Args&&... args)1259 {1260 return aux::bind_t<void, false, F, Args...>{forward<F>(f), forward<Args>(args)...};1261 }1262 1263 template<class R, class F, class... Args>1264 aux::bind_t<R, true, F, Args...> bind(F&& f, Args&&... args)1265 {1266 return aux::bind_t<R, true, F, Args...>{forward<F>(f), forward<Args>(args)...};1267 }1268 1269 namespace placeholders1270 {1271 /**1272 * Note: The number of placeholders is1273 * implementation defined, we've chosen1274 * 8 because it is a nice number1275 * and should be enough for any function1276 * call.1277 * Note: According to the C++14 standard, these1278 * are all extern non-const. We decided to use1279 * the C++17 form of them being inline constexpr1280 * because it is more convenient, makes sense1281 * and would eventually need to be upgraded1282 * anyway.1283 */1284 inline constexpr aux::placeholder_t<1> _1;1285 inline constexpr aux::placeholder_t<2> _2;1286 inline constexpr aux::placeholder_t<3> _3;1287 inline constexpr aux::placeholder_t<4> _4;1288 inline constexpr aux::placeholder_t<5> _5;1289 inline constexpr aux::placeholder_t<6> _6;1290 inline constexpr aux::placeholder_t<7> _7;1291 inline constexpr aux::placeholder_t<8> _8;1292 }1293 632 1294 633 /** -
uspace/lib/cpp/include/impl/tuple.hpp
rdaef596 rc866a83 31 31 32 32 #include <internal/aux.hpp> 33 #include <internal/tuple _cat.hpp>34 #include <internal/tuple _ops.hpp>33 #include <internal/tuple/tuple_cat.hpp> 34 #include <internal/tuple/tuple_ops.hpp> 35 35 #include <internal/type_transformation.hpp> 36 36 #include <functional> -
uspace/lib/cpp/include/impl/type_traits.hpp
rdaef596 rc866a83 33 33 #include <cstddef> 34 34 #include <internal/aux.hpp> 35 #include <internal/type_traits/references.hpp> 35 36 36 37 namespace std … … 893 894 894 895 /** 895 * 20.10.7.2, reference modifications:896 */897 898 template<class T>899 struct remove_reference: aux::type_is<T>900 { /* DUMMY BODY */ };901 902 template<class T>903 struct remove_reference<T&>: aux::type_is<T>904 { /* DUMMY BODY */ };905 906 template<class T>907 struct remove_reference<T&&>: aux::type_is<T>908 { /* DUMMY BODY */ };909 910 // TODO: is this good?911 template<class T>912 struct add_lvalue_reference: aux::type_is<T&>913 { /* DUMMY BODY */ };914 915 // TODO: Special case when T is not referencable!916 template<class T>917 struct add_rvalue_reference: aux::type_is<T&&>918 { /* DUMMY BODY */ };919 920 template<class T>921 struct add_rvalue_reference<T&>: aux::type_is<T&>922 { /* DUMMY BODY */ };923 924 template<class T>925 using remove_reference_t = typename remove_reference<T>::type;926 927 template<class T>928 using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;929 930 template<class T>931 using add_rvalue_reference_t = typename add_rvalue_reference<T>::type;932 933 /**934 896 * 20.10.7.3, sign modifications: 935 897 */ … … 1067 1029 struct underlying_type; 1068 1030 1069 template<class>1070 class result_of; // not defined1071 1072 template<class F, class... ArgTypes>1073 class result_of<F(ArgTypes...)>;1074 1075 1031 template<std::size_t Len, std::size_t Align = 0> 1076 1032 using aligned_storage_t = typename aligned_storage<Len, Align>::type; … … 1087 1043 template<class T> 1088 1044 using underlying_type_t = typename underlying_type<T>::type; 1089 1090 template<class T>1091 using result_of_t = typename result_of<T>::type;1092 1045 } 1093 1046 -
uspace/lib/cpp/include/impl/utility.hpp
rdaef596 rc866a83 32 32 #include <cstdint> 33 33 #include <internal/type_transformation.hpp> 34 #include <internal/utility/forward_move.hpp> 34 35 #include <type_traits> 35 36 … … 65 66 return !(lhs < rhs); 66 67 } 67 }68 69 /**70 * 20.2.4, forward/move helpers:71 */72 73 template<class T>74 constexpr T&& forward(remove_reference_t<T>& t) noexcept75 {76 return static_cast<T&&>(t);77 }78 79 template<class T>80 constexpr T&& forward(remove_reference_t<T>&& t) noexcept81 {82 return static_cast<T&&>(t);83 }84 85 template<class T>86 constexpr remove_reference_t<T>&& move(T&& t) noexcept87 {88 return static_cast<remove_reference_t<T>&&>(t);89 68 } 90 69 … … 124 103 return old_val; 125 104 } 126 127 /**128 * 20.2.5, function template declval:129 * Note: This function only needs declaration, not130 * implementation.131 */132 133 template<class T>134 add_rvalue_reference_t<T> declval() noexcept;135 105 136 106 /** -
uspace/lib/cpp/include/internal/tuple/tuple_cat.hpp
rdaef596 rc866a83 27 27 */ 28 28 29 #ifndef LIBCPP_ TUPLE_CAT30 #define LIBCPP_ TUPLE_CAT29 #ifndef LIBCPP_INTERNAL_TUPLE_TUPLE_CAT 30 #define LIBCPP_INTERNAL_TUPLE_TUPLE_CAT 31 31 32 32 #include <utility> -
uspace/lib/cpp/include/internal/tuple/tuple_ops.hpp
rdaef596 rc866a83 27 27 */ 28 28 29 #ifndef LIBCPP_ TUPLE_OPS30 #define LIBCPP_ TUPLE_OPS29 #ifndef LIBCPP_INTERNAL_TUPLE_TUPLE_OPS 30 #define LIBCPP_INTERNAL_TUPLE_TUPLE_OPS 31 31 32 32 #include <utility> -
uspace/lib/cpp/include/type_traits
rdaef596 rc866a83 28 28 29 29 #include <impl/type_traits.hpp> 30 #include <internal/type_traits/references.hpp> 31 #include <internal/type_traits/result_of.hpp> -
uspace/lib/cpp/include/utility
rdaef596 rc866a83 28 28 29 29 #include <impl/utility.hpp> 30 #include <internal/utility/declval.hpp> 31 #include <internal/utility/forward_move.hpp>
Note:
See TracChangeset
for help on using the changeset viewer.