From c4bc83ee9cbb82160410855116865d9767469d76 Mon Sep 17 00:00:00 2001 From: Bulat Gayazov Date: Fri, 26 Jul 2024 18:17:20 +0000 Subject: [PATCH 01/17] Returned util --- CMakeLists.txt | 1 + cmake/public_headers.txt | 84 + contrib/libs/CMakeLists.txt | 1 + contrib/libs/libc_compat/CMakeLists.txt | 23 + contrib/libs/libc_compat/README.md | 24 + contrib/libs/libc_compat/explicit_bzero.c | 58 + .../libc_compat/getservbyname/getservbyname.c | 11 + .../getservbyname/getservbyname_r.c | 55 + .../libs/libc_compat/getservbyname/lookup.h | 57 + .../libc_compat/getservbyname/lookup_serv.c | 113 + .../libc_compat/include/ifaddrs/ifaddrs.h | 54 + .../include/readpassphrase/readpassphrase.h | 44 + .../libc_compat/include/windows/sys/queue.h | 631 ++ .../libc_compat/include/windows/sys/uio.h | 25 + contrib/libs/libc_compat/memfd_create.c | 9 + contrib/libs/libc_compat/memrchr.c | 39 + contrib/libs/libc_compat/random/getentropy.c | 37 + contrib/libs/libc_compat/random/getrandom.c | 18 + contrib/libs/libc_compat/random/sys/random.h | 33 + contrib/libs/libc_compat/readpassphrase.c | 192 + .../libc_compat/reallocarray/reallocarray.c | 39 + .../libs/libc_compat/reallocarray/stdlib.h | 24 + .../libs/libc_compat/src/windows/sys/uio.c | 36 + contrib/libs/libc_compat/stpcpy.c | 44 + contrib/libs/libc_compat/strcasestr.c | 61 + contrib/libs/libc_compat/string.c | 16 + contrib/libs/libc_compat/string.h | 44 + contrib/libs/libc_compat/strlcat.c | 56 + contrib/libs/libc_compat/strlcpy.c | 51 + contrib/libs/libc_compat/strsep.c | 71 + contrib/libs/libc_compat/ubuntu_14/README.md | 91 + .../libc_compat/ubuntu_14/aligned_alloc.c | 6 + contrib/libs/libc_compat/ubuntu_14/c16rtomb.c | 35 + contrib/libs/libc_compat/ubuntu_14/c32rtomb.c | 7 + contrib/libs/libc_compat/ubuntu_14/features.h | 5 + .../libs/libc_compat/ubuntu_14/getauxval.cpp | 10 + contrib/libs/libc_compat/ubuntu_14/glibc.cpp | 111 + contrib/libs/libc_compat/ubuntu_14/glibc.h | 20 + contrib/libs/libc_compat/ubuntu_14/mbrtoc16.c | 30 + contrib/libs/libc_compat/ubuntu_14/mbrtoc32.c | 13 + .../libc_compat/ubuntu_14/secure_getenv.cpp | 12 + .../libs/libc_compat/ubuntu_14/timespec_get.c | 10 + contrib/libs/libc_compat/ubuntu_14/ya.make | 36 + contrib/libs/libc_compat/ya.make | 161 + .../library/string_utils/misc/misc.h | 4 +- include/ydb-cpp-sdk/stlfwd.h | 15 + src/CMakeLists.txt | 2 +- src/library/cppparser/parser.cpp | 4 +- src/library/cppparser/parser.h | 4 +- src/library/cpuid_check/cpu_id_check.cpp | 8 +- src/library/string_utils/base64/base64.cpp | 6 +- src/library/string_utils/quote/quote.cpp | 6 +- .../relaxed_escaper/relaxed_escaper.h | 6 +- src/library/string_utils/scan/CMakeLists.txt | 2 +- src/library/string_utils/stream/stream.h | 2 +- src/library/string_utils/url/CMakeLists.txt | 8 +- src/library/string_utils/url/url.cpp | 22 +- src/library/string_utils/url/url.h | 2 +- src/library/threading/CMakeLists.txt | 1 - .../threading/poor_man_openmp/CMakeLists.txt | 12 - .../poor_man_openmp/thread_helper.cpp | 7 - .../threading/poor_man_openmp/thread_helper.h | 104 - .../poor_man_openmp/thread_helper_ut.cpp | 26 - .../threading/poor_man_openmp/ut/ya.make | 7 - tests/some_code.cpp | 17 + tests/unit/CMakeLists.txt | 10 + .../enum_serialization_runtime/CMakeLists.txt | 1 - .../enum_runtime.cpp | 4 +- .../enum_serialization_runtime/enum_runtime.h | 2 +- .../ordered_pairs.h | 6 +- tools/enum_parser/parse_enum/CMakeLists.txt | 1 - tools/enum_parser/parse_enum/parse_enum.cpp | 19 +- tools/enum_parser/parse_enum/parse_enum.h | 4 +- util/CMakeLists.txt | 367 + util/README.md | 70 + util/charset/CMakeLists.txt | 35 + util/charset/generated/unidata.cpp | 7538 +++++++++++++++++ util/charset/recode_result.cpp | 1 + util/charset/recode_result.h | 9 + util/charset/unicode_table.cpp | 1 + util/charset/unicode_table.h | 123 + util/charset/unidata.cpp | 1 + util/charset/unidata.h | 421 + util/charset/ut/utf8/invalid_UTF8.bin | Bin 0 -> 419640 bytes util/charset/ut/utf8/test1.txt | 505 ++ util/charset/ut/ya.make | 14 + util/charset/utf8.cpp | 170 + util/charset/utf8.h | 431 + util/charset/utf8_ut.cpp | 126 + util/charset/wide.cpp | 626 ++ util/charset/wide.h | 880 ++ util/charset/wide_specific.h | 22 + util/charset/wide_sse41.cpp | 247 + util/charset/wide_ut.cpp | 1799 ++++ util/charset/ya.make | 32 + util/datetime/base.cpp | 335 + util/datetime/base.h | 823 ++ util/datetime/base.pxd | 126 + util/datetime/base_ut.cpp | 656 ++ util/datetime/benchmark/format/main.cpp | 59 + util/datetime/benchmark/format/ya.make | 10 + util/datetime/benchmark/gmtime_r/main.cpp | 44 + util/datetime/benchmark/gmtime_r/ya.make | 11 + util/datetime/benchmark/ya.make | 4 + util/datetime/constants.cpp | 1 + util/datetime/constants.h | 7 + util/datetime/cputimer.cpp | 150 + util/datetime/cputimer.h | 128 + util/datetime/cputimer_ut.cpp | 10 + util/datetime/parser.h | 294 + util/datetime/parser.rl6 | 802 ++ util/datetime/parser_deprecated_ut.cpp | 576 ++ util/datetime/parser_ut.cpp | 634 ++ util/datetime/process_uptime.cpp | 52 + util/datetime/process_uptime.h | 8 + util/datetime/process_uptime_ut.cpp | 17 + util/datetime/strptime.cpp | 627 ++ util/datetime/systime.cpp | 294 + util/datetime/systime.h | 51 + util/datetime/uptime.cpp | 60 + util/datetime/uptime.h | 8 + util/datetime/uptime_ut.cpp | 12 + util/datetime/ut/ya.make | 14 + util/datetime/ya.make | 9 + util/digest/benchmark/murmur/main.cpp | 27 + util/digest/benchmark/murmur/ya.make | 7 + util/digest/benchmark/ya.make | 3 + util/digest/city.cpp | 309 + util/digest/city.h | 83 + util/digest/city_ut.cpp | 15 + util/digest/fnv.cpp | 1 + util/digest/fnv.h | 73 + util/digest/fnv_ut.cpp | 23 + util/digest/multi.cpp | 1 + util/digest/multi.h | 14 + util/digest/multi.pxd | 2 + util/digest/multi_ut.cpp | 38 + util/digest/multi_ut.pyx | 19 + util/digest/murmur.cpp | 129 + util/digest/murmur.h | 52 + util/digest/murmur_ut.cpp | 85 + util/digest/numeric.cpp | 1 + util/digest/numeric.h | 93 + util/digest/sequence.cpp | 1 + util/digest/sequence.h | 48 + util/digest/sequence_ut.cpp | 62 + util/digest/ut/ya.make | 13 + util/digest/ut_cython/test_digest.py | 12 + util/digest/ut_cython/ya.make | 14 + util/digest/ya.make | 12 + util/draft/CMakeLists.txt | 11 + util/draft/date.cpp | 113 + util/draft/date.h | 129 + util/draft/date_ut.cpp | 36 + util/draft/datetime.cpp | 236 + util/draft/datetime.h | 184 + util/draft/datetime_ut.cpp | 231 + util/draft/enum.cpp | 1 + util/draft/enum.h | 136 + util/draft/holder_vector.cpp | 1 + util/draft/holder_vector.h | 102 + util/draft/holder_vector_ut.cpp | 69 + util/draft/ip.cpp | 1 + util/draft/ip.h | 131 + util/draft/matrix.cpp | 1 + util/draft/matrix.h | 108 + util/draft/memory.cpp | 1 + util/draft/memory.h | 40 + util/draft/memory_ut.cpp | 69 + util/draft/ut/ya.make | 18 + util/draft/ya.make | 23 + util/folder/dirent_win.c | 125 + util/folder/dirent_win.h | 46 + util/folder/dirut.cpp | 621 ++ util/folder/dirut.h | 119 + util/folder/dirut_ut.cpp | 132 + util/folder/filelist.cpp | 40 + util/folder/filelist.h | 81 + util/folder/filelist_ut.cpp | 56 + util/folder/fts.cpp | 1434 ++++ util/folder/fts.h | 108 + util/folder/fts_ut.cpp | 123 + util/folder/fwd.cpp | 1 + util/folder/fwd.h | 5 + util/folder/iterator.cpp | 11 + util/folder/iterator.h | 106 + util/folder/iterator_ut.cpp | 218 + util/folder/lstat_win.c | 35 + util/folder/lstat_win.h | 20 + util/folder/path.cpp | 537 ++ util/folder/path.h | 235 + util/folder/path.pxd | 93 + util/folder/path_ut.cpp | 897 ++ util/folder/path_ut.pyx | 380 + util/folder/pathsplit.cpp | 149 + util/folder/pathsplit.h | 113 + util/folder/pathsplit_ut.cpp | 482 ++ util/folder/tempdir.cpp | 44 + util/folder/tempdir.h | 43 + util/folder/ut/ya.make | 18 + util/folder/ut_cython/test_folder.py | 12 + util/folder/ut_cython/ya.make | 14 + util/folder/ya.make | 9 + util/generic/adaptor.cpp | 1 + util/generic/adaptor.h | 140 + util/generic/adaptor_ut.cpp | 124 + util/generic/algorithm.cpp | 1 + util/generic/algorithm.h | 800 ++ util/generic/algorithm_ut.cpp | 906 ++ util/generic/array_ref.cpp | 1 + util/generic/array_ref.h | 283 + util/generic/array_ref.pxd | 25 + util/generic/array_ref_ut.cpp | 322 + util/generic/array_ref_ut.pyx | 28 + util/generic/array_size.cpp | 1 + util/generic/array_size.h | 24 + util/generic/array_size_ut.cpp | 22 + util/generic/bitmap.cpp | 1 + util/generic/bitmap.h | 1115 +++ util/generic/bitmap_ut.cpp | 597 ++ util/generic/bitops.cpp | 141 + util/generic/bitops.h | 459 + util/generic/bitops_ut.cpp | 348 + util/generic/bt_exception.cpp | 1 + util/generic/bt_exception.h | 24 + util/generic/buffer.cpp | 99 + util/generic/buffer.h | 246 + util/generic/buffer_ut.cpp | 205 + util/generic/cast.cpp | 1 + util/generic/cast.h | 176 + util/generic/cast_ut.cpp | 112 + util/generic/deque.cpp | 1 + util/generic/deque.h | 25 + util/generic/deque.pxd | 9 + util/generic/deque_ut.cpp | 245 + util/generic/deque_ut.pyx | 66 + util/generic/enum_range.cpp | 1 + util/generic/enum_range.h | 72 + util/generic/enum_range_ut.cpp | 237 + util/generic/explicit_type.cpp | 1 + util/generic/explicit_type.h | 42 + util/generic/explicit_type_ut.cpp | 54 + util/generic/fastqueue.cpp | 1 + util/generic/fastqueue.h | 54 + util/generic/flags.cpp | 29 + util/generic/flags.h | 258 + util/generic/flags_ut.cpp | 127 + util/generic/function.cpp | 1 + util/generic/function.h | 118 + util/generic/function_ref.cpp | 1 + util/generic/function_ref.h | 139 + util/generic/function_ref_ut.cpp | 150 + util/generic/function_ut.cpp | 138 + util/generic/fwd.cpp | 1 + util/generic/fwd.h | 168 + util/generic/guid.cpp | 186 + util/generic/guid.h | 79 + util/generic/guid_ut.cpp | 128 + util/generic/hash.cpp | 1 + util/generic/hash.h | 355 + util/generic/hash.pxd | 66 + util/generic/hash_multi_map.cpp | 1 + util/generic/hash_multi_map.h | 278 + util/generic/hash_primes.cpp | 121 + util/generic/hash_primes.h | 138 + util/generic/hash_primes_ut.cpp | 80 + util/generic/hash_set.cpp | 1 + util/generic/hash_set.h | 490 ++ util/generic/hash_set.pxd | 43 + util/generic/hash_set_ut.pyx | 53 + util/generic/hash_table.cpp | 51 + util/generic/hash_table.h | 1440 ++++ util/generic/hash_ut.cpp | 1359 +++ util/generic/hash_ut.pyx | 84 + util/generic/hide_ptr.cpp | 5 + util/generic/hide_ptr.h | 3 + util/generic/intrlist.cpp | 1 + util/generic/intrlist.h | 893 ++ util/generic/intrlist_ut.cpp | 512 ++ util/generic/is_in.cpp | 1 + util/generic/is_in.h | 53 + util/generic/is_in_ut.cpp | 117 + util/generic/iterator.cpp | 1 + util/generic/iterator.h | 139 + util/generic/iterator_range.cpp | 1 + util/generic/iterator_range.h | 104 + util/generic/iterator_range_ut.cpp | 98 + util/generic/iterator_ut.cpp | 63 + util/generic/lazy_value.cpp | 1 + util/generic/lazy_value.h | 62 + util/generic/lazy_value_ut.cpp | 157 + util/generic/list.cpp | 1 + util/generic/list.h | 22 + util/generic/list.pxd | 65 + util/generic/list_ut.cpp | 14 + util/generic/list_ut.pyx | 158 + util/generic/map.cpp | 1 + util/generic/map.h | 36 + util/generic/map.pxd | 46 + util/generic/map_ut.cpp | 496 ++ util/generic/map_ut.pyx | 84 + util/generic/mapfindptr.cpp | 1 + util/generic/mapfindptr.h | 61 + util/generic/mapfindptr_ut.cpp | 68 + util/generic/maybe.cpp | 16 + util/generic/maybe.h | 868 ++ util/generic/maybe.pxd | 35 + util/generic/maybe_traits.h | 185 + util/generic/maybe_ut.cpp | 1126 +++ util/generic/maybe_ut.pyx | 185 + util/generic/mem_copy.cpp | 1 + util/generic/mem_copy.h | 55 + util/generic/mem_copy_ut.cpp | 113 + util/generic/noncopyable.cpp | 1 + util/generic/noncopyable.h | 38 + util/generic/object_counter.cpp | 1 + util/generic/object_counter.h | 54 + util/generic/objects_counter_ut.cpp | 36 + util/generic/overloaded.cpp | 1 + util/generic/overloaded.h | 56 + util/generic/overloaded_ut.cpp | 82 + util/generic/ptr.cpp | 17 + util/generic/ptr.h | 1174 +++ util/generic/ptr.pxd | 42 + util/generic/ptr_ut.cpp | 936 ++ util/generic/ptr_ut.pyx | 26 + util/generic/queue.cpp | 1 + util/generic/queue.h | 58 + util/generic/queue_ut.cpp | 210 + util/generic/refcount.cpp | 1 + util/generic/refcount.h | 168 + util/generic/reserve.h | 11 + util/generic/scope.cpp | 1 + util/generic/scope.h | 65 + util/generic/scope_ut.cpp | 47 + .../generic}/serialized_enum.cpp | 0 .../generic}/serialized_enum.h | 35 +- .../generic}/serialized_enum_ut.cpp | 22 +- util/generic/set.cpp | 1 + util/generic/set.h | 32 + util/generic/set_ut.cpp | 408 + util/generic/singleton.cpp | 62 + util/generic/singleton.h | 137 + util/generic/singleton_ut.cpp | 46 + util/generic/size_literals.cpp | 1 + util/generic/size_literals.h | 69 + util/generic/size_literals_ut.cpp | 68 + util/generic/stack.cpp | 1 + util/generic/stack.h | 18 + util/generic/stack_ut.cpp | 16 + util/generic/store_policy.cpp | 1 + util/generic/store_policy.h | 116 + util/generic/store_policy_ut.cpp | 117 + util/generic/strbase.h | 554 ++ util/generic/strbuf.cpp | 8 + util/generic/strbuf.h | 549 ++ util/generic/strbuf_ut.cpp | 391 + util/generic/strfcpy.cpp | 50 + util/generic/strfcpy.h | 17 + util/generic/string.cpp | 155 + util/generic/string.h | 1315 +++ util/generic/string.pxd | 118 + util/generic/string_hash.h | 22 + util/generic/string_transparent_hash_ut.cpp | 19 + util/generic/string_ut.cpp | 1246 +++ util/generic/string_ut.h | 1162 +++ util/generic/string_ut.pyx | 225 + util/generic/typelist.cpp | 5 + util/generic/typelist.h | 114 + util/generic/typelist_ut.cpp | 84 + util/generic/typetraits.cpp | 1 + util/generic/typetraits.h | 334 + util/generic/typetraits_ut.cpp | 478 ++ util/generic/ut/ya.make | 72 + util/generic/ut_cython/test_generic.py | 28 + util/generic/ut_cython/ya.make | 23 + util/generic/utility.cpp | 21 + util/generic/utility.h | 134 + util/generic/utility_ut.cpp | 180 + util/generic/va_args.cpp | 15 + util/generic/va_args.h | 791 ++ util/generic/va_args_ut.cpp | 106 + util/generic/variant.cpp | 1 + util/generic/variant.h | 23 + util/generic/vector.cpp | 1 + util/generic/vector.h | 132 + util/generic/vector.pxd | 83 + util/generic/vector_ut.cpp | 587 ++ util/generic/vector_ut.pyx | 221 + util/generic/xrange.cpp | 1 + util/generic/xrange.h | 268 + util/generic/xrange_ut.cpp | 217 + util/generic/ya.make | 9 + util/generic/yexception.cpp | 173 + util/generic/yexception.h | 245 + util/generic/yexception_ut.c | 5 + util/generic/yexception_ut.cpp | 449 + util/generic/yexception_ut.h | 14 + util/generic/ylimits.cpp | 1 + util/generic/ylimits.h | 92 + util/generic/ylimits_ut.cpp | 156 + util/generic/ymath.cpp | 56 + util/generic/ymath.h | 210 + util/generic/ymath_ut.cpp | 220 + util/memory/addstorage.cpp | 1 + util/memory/addstorage.h | 93 + util/memory/addstorage_ut.cpp | 24 + util/memory/alloc.cpp | 20 + util/memory/alloc.h | 27 + util/memory/blob.cpp | 418 + util/memory/blob.h | 326 + util/memory/blob.pxd | 41 + util/memory/blob_ut.cpp | 79 + util/memory/blob_ut.pyx | 141 + util/memory/mmapalloc.cpp | 33 + util/memory/mmapalloc.h | 8 + util/memory/pool.cpp | 58 + util/memory/pool.h | 435 + util/memory/pool_ut.cpp | 285 + util/memory/segmented_string_pool.cpp | 1 + util/memory/segmented_string_pool.h | 194 + util/memory/segpool_alloc.cpp | 1 + util/memory/segpool_alloc.h | 117 + util/memory/smallobj.cpp | 1 + util/memory/smallobj.h | 141 + util/memory/smallobj_ut.cpp | 87 + util/memory/tempbuf.cpp | 321 + util/memory/tempbuf.h | 103 + util/memory/tempbuf_ut.cpp | 93 + util/memory/ut/ya.make | 13 + util/memory/ut_cython/test_memory.py | 12 + util/memory/ut_cython/ya.make | 14 + util/memory/ya.make | 9 + util/network/address.cpp | 223 + util/network/address.h | 149 + util/network/address_ut.cpp | 39 + util/network/endpoint.cpp | 67 + util/network/endpoint.h | 61 + util/network/endpoint_ut.cpp | 123 + util/network/hostip.cpp | 76 + util/network/hostip.h | 16 + util/network/init.cpp | 29 + util/network/init.h | 60 + util/network/interface.cpp | 81 + util/network/interface.h | 17 + util/network/iovec.cpp | 1 + util/network/iovec.h | 65 + util/network/ip.cpp | 1 + util/network/ip.h | 119 + util/network/ip_ut.cpp | 63 + util/network/nonblock.cpp | 104 + util/network/nonblock.h | 8 + util/network/pair.cpp | 97 + util/network/pair.h | 9 + util/network/poller.cpp | 86 + util/network/poller.h | 58 + util/network/poller_ut.cpp | 243 + util/network/pollerimpl.cpp | 1 + util/network/pollerimpl.h | 715 ++ util/network/sock.cpp | 1 + util/network/sock.h | 626 ++ util/network/sock_ut.cpp | 179 + util/network/socket.cpp | 1255 +++ util/network/socket.h | 427 + util/network/socket_ut.cpp | 312 + util/network/ut/ya.make | 20 + util/network/ya.make | 3 + util/random/common_ops.cpp | 1 + util/random/common_ops.h | 130 + util/random/common_ops_ut.cpp | 69 + util/random/easy.cpp | 1 + util/random/easy.h | 47 + util/random/easy_ut.cpp | 35 + util/random/entropy.cpp | 221 + util/random/entropy.h | 21 + util/random/entropy_ut.cpp | 13 + util/random/fast.cpp | 52 + util/random/fast.h | 101 + util/random/fast_ut.cpp | 119 + util/random/init_atfork.cpp | 29 + util/random/init_atfork.h | 3 + util/random/lcg_engine.cpp | 30 + util/random/lcg_engine.h | 66 + util/random/mersenne.cpp | 1 + util/random/mersenne.h | 46 + util/random/mersenne32.cpp | 98 + util/random/mersenne32.h | 50 + util/random/mersenne64.cpp | 100 + util/random/mersenne64.h | 50 + util/random/mersenne_ut.cpp | 82 + util/random/normal.cpp | 27 + util/random/normal.h | 38 + util/random/normal_ut.cpp | 81 + util/random/random.cpp | 125 + util/random/random.h | 30 + util/random/random_ut.cpp | 155 + util/random/shuffle.cpp | 1 + util/random/shuffle.h | 77 + util/random/shuffle_ut.cpp | 114 + util/random/ut/ya.make | 16 + util/random/ya.make | 3 + util/str_stl.cpp | 1 + util/str_stl.h | 284 + util/stream/aligned.cpp | 30 + util/stream/aligned.h | 99 + util/stream/aligned_ut.cpp | 63 + util/stream/buffer.cpp | 119 + util/stream/buffer.h | 119 + util/stream/buffer_ut.cpp | 85 + util/stream/buffered.cpp | 428 + util/stream/buffered.h | 235 + util/stream/buffered_ut.cpp | 142 + util/stream/debug.cpp | 50 + util/stream/debug.h | 53 + util/stream/direct_io.cpp | 47 + util/stream/direct_io.h | 43 + util/stream/direct_io_ut.cpp | 70 + util/stream/file.cpp | 111 + util/stream/file.h | 116 + util/stream/file_ut.cpp | 74 + util/stream/format.cpp | 134 + util/stream/format.h | 470 + util/stream/format_std_ut.cpp | 29 + util/stream/format_ut.cpp | 182 + util/stream/fwd.cpp | 1 + util/stream/fwd.h | 100 + util/stream/hex.cpp | 30 + util/stream/hex.h | 8 + util/stream/hex_ut.cpp | 29 + util/stream/holder.cpp | 1 + util/stream/holder.h | 44 + util/stream/input.cpp | 344 + util/stream/input.h | 273 + util/stream/input_ut.cpp | 157 + util/stream/ios_ut.cpp | 496 ++ util/stream/labeled.cpp | 1 + util/stream/labeled.h | 19 + util/stream/labeled_ut.cpp | 12 + util/stream/length.cpp | 47 + util/stream/length.h | 100 + util/stream/length_ut.cpp | 52 + util/stream/mem.cpp | 65 + util/stream/mem.h | 261 + util/stream/mem_ut.cpp | 78 + util/stream/multi.cpp | 56 + util/stream/multi.h | 32 + util/stream/multi_ut.cpp | 50 + util/stream/null.cpp | 36 + util/stream/null.h | 61 + util/stream/output.cpp | 460 + util/stream/output.h | 304 + util/stream/output.pxd | 12 + util/stream/pipe.cpp | 121 + util/stream/pipe.h | 112 + util/stream/printf.cpp | 50 + util/stream/printf.h | 25 + util/stream/printf_ut.cpp | 33 + util/stream/str.cpp | 44 + util/stream/str.h | 246 + util/stream/str.pxd | 12 + util/stream/str_ut.cpp | 225 + util/stream/str_ut.pyx | 62 + util/stream/tee.cpp | 24 + util/stream/tee.h | 28 + util/stream/tempbuf.cpp | 22 + util/stream/tempbuf.h | 21 + util/stream/tokenizer.cpp | 1 + util/stream/tokenizer.h | 214 + util/stream/tokenizer_ut.cpp | 264 + util/stream/trace.cpp | 1 + util/stream/trace.h | 48 + util/stream/ut/ya.make | 28 + util/stream/ut_cython/test_stream.py | 12 + util/stream/ut_cython/ya.make | 14 + util/stream/walk.cpp | 22 + util/stream/walk.h | 35 + util/stream/walk_ut.cpp | 55 + util/stream/ya.make | 9 + util/stream/zerocopy.cpp | 61 + util/stream/zerocopy.h | 91 + util/stream/zerocopy_output.cpp | 18 + util/stream/zerocopy_output.h | 57 + util/stream/zerocopy_output_ut.cpp | 48 + util/stream/zlib.cpp | 379 + util/stream/zlib.h | 173 + util/stream/zlib_ut.cpp | 229 + util/string/ascii.cpp | 59 + util/string/ascii.h | 253 + util/string/ascii_ut.cpp | 98 + util/string/builder.cpp | 8 + util/string/builder.h | 39 + util/string/builder_ut.cpp | 63 + util/string/cast.cpp | 781 ++ util/string/cast.h | 479 ++ util/string/cast.pxd | 10 + util/string/cast_ut.cpp | 619 ++ util/string/cast_ut.pyx | 13 + util/string/cstriter.cpp | 1 + util/string/cstriter.h | 14 + util/string/escape.cpp | 433 + util/string/escape.h | 70 + util/string/escape_ut.cpp | 148 + util/string/hex.cpp | 63 + util/string/hex.h | 59 + util/string/hex_ut.cpp | 19 + util/string/join.cpp | 1 + util/string/join.h | 265 + util/string/join_ut.cpp | 163 + util/string/printf.cpp | 38 + util/string/printf.h | 13 + util/string/printf_ut.cpp | 30 + util/string/reverse.cpp | 33 + util/string/reverse.h | 16 + util/string/split.cpp | 24 + util/string/split.h | 1071 +++ util/string/split_ut.cpp | 834 ++ util/string/strip.cpp | 19 + util/string/strip.h | 282 + util/string/strip_ut.cpp | 187 + util/string/strspn.cpp | 1 + util/string/strspn.h | 65 + util/string/strspn_ut.cpp | 73 + util/string/subst.cpp | 201 + util/string/subst.h | 56 + util/string/subst_ut.cpp | 253 + util/string/type.cpp | 86 + util/string/type.h | 42 + util/string/type_ut.cpp | 76 + util/string/ut/ya.make | 22 + util/string/ut_cython/test_string.py | 13 + util/string/ut_cython/ya.make | 14 + util/string/util.cpp | 72 + util/string/util.h | 195 + util/string/util_ut.cpp | 46 + util/string/vector.cpp | 91 + util/string/vector.h | 132 + util/string/vector_ut.cpp | 37 + util/string/ya.make | 9 + util/system/align.cpp | 1 + util/system/align.h | 49 + util/system/align_ut.cpp | 35 + util/system/atexit.cpp | 148 + util/system/atexit.h | 31 + util/system/atexit_ut.cpp | 104 + util/system/backtrace.cpp | 306 + util/system/backtrace.h | 44 + util/system/backtrace_ut.cpp | 85 + util/system/byteorder.cpp | 1 + util/system/byteorder.h | 141 + util/system/byteorder_ut.cpp | 26 + util/system/compat.cpp | 37 + util/system/compat.h | 86 + util/system/compat_ut.cpp | 12 + util/system/compiler.cpp | 9 + util/system/compiler.h | 718 ++ util/system/compiler_ut.cpp | 72 + util/system/condvar.cpp | 145 + util/system/condvar.h | 71 + util/system/condvar_ut.cpp | 191 + util/system/context.cpp | 339 + util/system/context.h | 198 + util/system/context_aarch64.S | 52 + util/system/context_aarch64.h | 8 + util/system/context_i686.asm | 43 + util/system/context_i686.h | 9 + util/system/context_ut.cpp | 71 + util/system/context_x86.asm | 15 + util/system/context_x86.h | 12 + util/system/context_x86_64.asm | 40 + util/system/context_x86_64.h | 7 + util/system/cpu_id.cpp | 263 + util/system/cpu_id.h | 157 + util/system/cpu_id_ut.cpp | 450 + util/system/daemon.cpp | 167 + util/system/daemon.h | 27 + util/system/daemon_ut.cpp | 94 + util/system/datetime.cpp | 107 + util/system/datetime.h | 104 + util/system/datetime_ut.cpp | 1 + util/system/defaults.c | 2 + util/system/defaults.h | 155 + util/system/demangle_impl.h | 21 + util/system/direct_io.cpp | 266 + util/system/direct_io.h | 75 + util/system/direct_io_ut.cpp | 115 + util/system/dynlib.cpp | 138 + util/system/dynlib.h | 119 + util/system/env.cpp | 93 + util/system/env.h | 62 + util/system/env_ut.cpp | 59 + util/system/err.cpp | 80 + util/system/error.cpp | 92 + util/system/error.h | 95 + util/system/error_ut.cpp | 41 + util/system/event.cpp | 135 + util/system/event.h | 122 + util/system/event_ut.cpp | 127 + util/system/execpath.cpp | 189 + util/system/execpath.h | 17 + util/system/execpath_ut.cpp | 21 + util/system/fasttime.cpp | 240 + util/system/fasttime.h | 6 + util/system/fhandle.cpp | 1 + util/system/fhandle.h | 27 + util/system/file.cpp | 1400 +++ util/system/file.h | 230 + util/system/file_lock.cpp | 46 + util/system/file_lock.h | 34 + util/system/file_ut.cpp | 416 + util/system/filemap.cpp | 585 ++ util/system/filemap.h | 381 + util/system/filemap_ut.cpp | 359 + util/system/flock.cpp | 71 + util/system/flock.h | 35 + util/system/flock_ut.cpp | 57 + util/system/fs.cpp | 197 + util/system/fs.h | 164 + util/system/fs_ut.cpp | 323 + util/system/fs_win.cpp | 258 + util/system/fs_win.h | 31 + util/system/fs_win_ut.cpp | 71 + util/system/fstat.cpp | 278 + util/system/fstat.h | 52 + util/system/fstat_ut.cpp | 228 + util/system/getpid.cpp | 22 + util/system/getpid.h | 12 + util/system/getpid_ut.cpp | 19 + util/system/guard.cpp | 1 + util/system/guard.h | 177 + util/system/guard_ut.cpp | 180 + util/system/hi_lo.cpp | 1 + util/system/hi_lo.h | 149 + util/system/hi_lo_ut.cpp | 69 + util/system/hostname.cpp | 115 + util/system/hostname.h | 10 + util/system/hostname_ut.cpp | 25 + util/system/hp_timer.cpp | 118 + util/system/hp_timer.h | 36 + util/system/info.cpp | 316 + util/system/info.h | 14 + util/system/info_ut.cpp | 22 + util/system/interrupt_signals.cpp | 57 + util/system/interrupt_signals.h | 22 + util/system/interrupt_signals_ut.cpp | 46 + util/system/madvise.cpp | 125 + util/system/madvise.h | 30 + util/system/maxlen.cpp | 1 + util/system/maxlen.h | 32 + util/system/mem_info.cpp | 215 + util/system/mem_info.h | 18 + util/system/mem_info_ut.cpp | 21 + util/system/mincore.cpp | 33 + util/system/mincore.h | 38 + util/system/mincore_ut.cpp | 39 + util/system/mktemp.cpp | 66 + util/system/mktemp_system.cpp | 170 + util/system/mktemp_ut.cpp | 41 + util/system/mlock.cpp | 101 + util/system/mlock.h | 43 + util/system/mutex.cpp | 146 + util/system/mutex.h | 64 + util/system/mutex_ut.cpp | 128 + util/system/nice.cpp | 15 + util/system/nice.h | 3 + util/system/nice_ut.cpp | 42 + util/system/pipe.cpp | 160 + util/system/pipe.h | 90 + util/system/pipe_ut.cpp | 15 + util/system/platform.cpp | 18 + util/system/platform.h | 251 + util/system/platform_ut.cpp | 26 + util/system/progname.cpp | 26 + util/system/progname.h | 13 + util/system/progname_ut.cpp | 18 + util/system/protect.cpp | 94 + util/system/protect.h | 25 + util/system/rusage.cpp | 118 + util/system/rusage.h | 26 + util/system/rusage_ut.cpp | 11 + util/system/rwlock.cpp | 251 + util/system/rwlock.h | 78 + util/system/rwlock_ut.cpp | 118 + util/system/sanitizers.cpp | 75 + util/system/sanitizers.h | 199 + util/system/sanitizers_ut.cpp | 15 + util/system/sem.cpp | 278 + util/system/sem.h | 41 + util/system/shellcommand.cpp | 1166 +++ util/system/shellcommand.h | 503 ++ util/system/shellcommand_ut.cpp | 492 ++ util/system/shmat.cpp | 231 + util/system/shmat.h | 32 + util/system/shmat_ut.cpp | 17 + util/system/sigset.cpp | 1 + util/system/sigset.h | 78 + util/system/spin_wait.cpp | 42 + util/system/spin_wait.h | 10 + util/system/spinlock.cpp | 1 + util/system/spinlock.h | 127 + util/system/spinlock_ut.cpp | 37 + util/system/src_location.cpp | 15 + util/system/src_location.h | 25 + util/system/src_location_ut.cpp | 18 + util/system/src_root.h | 68 + util/system/src_root_ut.cpp | 27 + util/system/sys_alloc.cpp | 1 + util/system/sys_alloc.h | 43 + util/system/sysstat.cpp | 47 + util/system/sysstat.h | 52 + util/system/tempfile.cpp | 25 + util/system/tempfile.h | 50 + util/system/tempfile_ut.cpp | 151 + util/system/thread.cpp | 578 ++ util/system/thread.h | 175 + util/system/thread.i | 47 + util/system/thread_ut.cpp | 229 + util/system/tls.cpp | 259 + util/system/tls.h | 307 + util/system/tls_ut.cpp | 58 + util/system/type_name.cpp | 59 + util/system/type_name.h | 30 + util/system/type_name_ut.cpp | 184 + util/system/types.cpp | 18 + util/system/types.h | 125 + util/system/types.pxd | 13 + util/system/types_ut.cpp | 23 + util/system/types_ut.pyx | 40 + util/system/unaligned_mem.cpp | 1 + util/system/unaligned_mem.h | 67 + util/system/unaligned_mem_ut.cpp | 96 + util/system/user.cpp | 56 + util/system/user.h | 5 + util/system/user_ut.cpp | 9 + util/system/ut/stdin_osfhandle/main.cpp | 15 + util/system/ut/stdin_osfhandle/ya.make | 11 + util/system/ut/ya.make | 98 + util/system/ut_cython/test_system.py | 12 + util/system/ut_cython/ya.make | 14 + util/system/utime.cpp | 18 + util/system/utime.h | 6 + util/system/valgrind.cpp | 1 + util/system/valgrind.h | 48 + util/system/win_undef.h | 40 + util/system/winint.cpp | 1 + util/system/winint.h | 16 + util/system/ya.make | 9 + util/system/yassert.cpp | 94 + util/system/yassert.h | 105 + util/system/yassert_ut.cpp | 41 + util/system/yield.cpp | 24 + util/system/yield.h | 4 + util/tests/ya_util_tests.inc | 4 + util/thread/factory.cpp | 93 + util/thread/factory.h | 65 + util/thread/factory_ut.cpp | 57 + util/thread/fwd.cpp | 1 + util/thread/fwd.h | 30 + util/thread/lfqueue.cpp | 1 + util/thread/lfqueue.h | 399 + util/thread/lfqueue_ut.cpp | 331 + util/thread/lfstack.cpp | 1 + util/thread/lfstack.h | 192 + util/thread/lfstack_ut.cpp | 347 + util/thread/pool.cpp | 775 ++ util/thread/pool.h | 390 + util/thread/pool_ut.cpp | 256 + util/thread/singleton.cpp | 1 + util/thread/singleton.h | 41 + util/thread/singleton_ut.cpp | 21 + util/thread/ut/ya.make | 15 + util/thread/ya.make | 3 + util/ut/ya.make | 9 + util/ya.make | 412 + util/ysafeptr.cpp | 33 + util/ysafeptr.h | 432 + util/ysaveload.cpp | 22 + util/ysaveload.h | 769 ++ util/ysaveload_ut.cpp | 519 ++ 878 files changed, 120573 insertions(+), 242 deletions(-) create mode 100644 cmake/public_headers.txt create mode 100644 contrib/libs/libc_compat/CMakeLists.txt create mode 100644 contrib/libs/libc_compat/README.md create mode 100644 contrib/libs/libc_compat/explicit_bzero.c create mode 100644 contrib/libs/libc_compat/getservbyname/getservbyname.c create mode 100644 contrib/libs/libc_compat/getservbyname/getservbyname_r.c create mode 100644 contrib/libs/libc_compat/getservbyname/lookup.h create mode 100644 contrib/libs/libc_compat/getservbyname/lookup_serv.c create mode 100644 contrib/libs/libc_compat/include/ifaddrs/ifaddrs.h create mode 100644 contrib/libs/libc_compat/include/readpassphrase/readpassphrase.h create mode 100644 contrib/libs/libc_compat/include/windows/sys/queue.h create mode 100644 contrib/libs/libc_compat/include/windows/sys/uio.h create mode 100644 contrib/libs/libc_compat/memfd_create.c create mode 100644 contrib/libs/libc_compat/memrchr.c create mode 100644 contrib/libs/libc_compat/random/getentropy.c create mode 100644 contrib/libs/libc_compat/random/getrandom.c create mode 100644 contrib/libs/libc_compat/random/sys/random.h create mode 100644 contrib/libs/libc_compat/readpassphrase.c create mode 100644 contrib/libs/libc_compat/reallocarray/reallocarray.c create mode 100644 contrib/libs/libc_compat/reallocarray/stdlib.h create mode 100644 contrib/libs/libc_compat/src/windows/sys/uio.c create mode 100644 contrib/libs/libc_compat/stpcpy.c create mode 100644 contrib/libs/libc_compat/strcasestr.c create mode 100644 contrib/libs/libc_compat/string.c create mode 100644 contrib/libs/libc_compat/string.h create mode 100644 contrib/libs/libc_compat/strlcat.c create mode 100644 contrib/libs/libc_compat/strlcpy.c create mode 100644 contrib/libs/libc_compat/strsep.c create mode 100644 contrib/libs/libc_compat/ubuntu_14/README.md create mode 100644 contrib/libs/libc_compat/ubuntu_14/aligned_alloc.c create mode 100644 contrib/libs/libc_compat/ubuntu_14/c16rtomb.c create mode 100644 contrib/libs/libc_compat/ubuntu_14/c32rtomb.c create mode 100644 contrib/libs/libc_compat/ubuntu_14/features.h create mode 100644 contrib/libs/libc_compat/ubuntu_14/getauxval.cpp create mode 100644 contrib/libs/libc_compat/ubuntu_14/glibc.cpp create mode 100644 contrib/libs/libc_compat/ubuntu_14/glibc.h create mode 100644 contrib/libs/libc_compat/ubuntu_14/mbrtoc16.c create mode 100644 contrib/libs/libc_compat/ubuntu_14/mbrtoc32.c create mode 100644 contrib/libs/libc_compat/ubuntu_14/secure_getenv.cpp create mode 100644 contrib/libs/libc_compat/ubuntu_14/timespec_get.c create mode 100644 contrib/libs/libc_compat/ubuntu_14/ya.make create mode 100644 contrib/libs/libc_compat/ya.make create mode 100644 include/ydb-cpp-sdk/stlfwd.h delete mode 100644 src/library/threading/poor_man_openmp/CMakeLists.txt delete mode 100644 src/library/threading/poor_man_openmp/thread_helper.cpp delete mode 100644 src/library/threading/poor_man_openmp/thread_helper.h delete mode 100644 src/library/threading/poor_man_openmp/thread_helper_ut.cpp delete mode 100644 src/library/threading/poor_man_openmp/ut/ya.make create mode 100644 tests/some_code.cpp create mode 100644 util/CMakeLists.txt create mode 100644 util/README.md create mode 100644 util/charset/CMakeLists.txt create mode 100644 util/charset/generated/unidata.cpp create mode 100644 util/charset/recode_result.cpp create mode 100644 util/charset/recode_result.h create mode 100644 util/charset/unicode_table.cpp create mode 100644 util/charset/unicode_table.h create mode 100644 util/charset/unidata.cpp create mode 100644 util/charset/unidata.h create mode 100644 util/charset/ut/utf8/invalid_UTF8.bin create mode 100644 util/charset/ut/utf8/test1.txt create mode 100644 util/charset/ut/ya.make create mode 100644 util/charset/utf8.cpp create mode 100644 util/charset/utf8.h create mode 100644 util/charset/utf8_ut.cpp create mode 100644 util/charset/wide.cpp create mode 100644 util/charset/wide.h create mode 100644 util/charset/wide_specific.h create mode 100644 util/charset/wide_sse41.cpp create mode 100644 util/charset/wide_ut.cpp create mode 100644 util/charset/ya.make create mode 100644 util/datetime/base.cpp create mode 100644 util/datetime/base.h create mode 100644 util/datetime/base.pxd create mode 100644 util/datetime/base_ut.cpp create mode 100644 util/datetime/benchmark/format/main.cpp create mode 100644 util/datetime/benchmark/format/ya.make create mode 100644 util/datetime/benchmark/gmtime_r/main.cpp create mode 100644 util/datetime/benchmark/gmtime_r/ya.make create mode 100644 util/datetime/benchmark/ya.make create mode 100644 util/datetime/constants.cpp create mode 100644 util/datetime/constants.h create mode 100644 util/datetime/cputimer.cpp create mode 100644 util/datetime/cputimer.h create mode 100644 util/datetime/cputimer_ut.cpp create mode 100644 util/datetime/parser.h create mode 100644 util/datetime/parser.rl6 create mode 100644 util/datetime/parser_deprecated_ut.cpp create mode 100644 util/datetime/parser_ut.cpp create mode 100644 util/datetime/process_uptime.cpp create mode 100644 util/datetime/process_uptime.h create mode 100644 util/datetime/process_uptime_ut.cpp create mode 100644 util/datetime/strptime.cpp create mode 100644 util/datetime/systime.cpp create mode 100644 util/datetime/systime.h create mode 100644 util/datetime/uptime.cpp create mode 100644 util/datetime/uptime.h create mode 100644 util/datetime/uptime_ut.cpp create mode 100644 util/datetime/ut/ya.make create mode 100644 util/datetime/ya.make create mode 100644 util/digest/benchmark/murmur/main.cpp create mode 100644 util/digest/benchmark/murmur/ya.make create mode 100644 util/digest/benchmark/ya.make create mode 100644 util/digest/city.cpp create mode 100644 util/digest/city.h create mode 100644 util/digest/city_ut.cpp create mode 100644 util/digest/fnv.cpp create mode 100644 util/digest/fnv.h create mode 100644 util/digest/fnv_ut.cpp create mode 100644 util/digest/multi.cpp create mode 100644 util/digest/multi.h create mode 100644 util/digest/multi.pxd create mode 100644 util/digest/multi_ut.cpp create mode 100644 util/digest/multi_ut.pyx create mode 100644 util/digest/murmur.cpp create mode 100644 util/digest/murmur.h create mode 100644 util/digest/murmur_ut.cpp create mode 100644 util/digest/numeric.cpp create mode 100644 util/digest/numeric.h create mode 100644 util/digest/sequence.cpp create mode 100644 util/digest/sequence.h create mode 100644 util/digest/sequence_ut.cpp create mode 100644 util/digest/ut/ya.make create mode 100644 util/digest/ut_cython/test_digest.py create mode 100644 util/digest/ut_cython/ya.make create mode 100644 util/digest/ya.make create mode 100644 util/draft/CMakeLists.txt create mode 100644 util/draft/date.cpp create mode 100644 util/draft/date.h create mode 100644 util/draft/date_ut.cpp create mode 100644 util/draft/datetime.cpp create mode 100644 util/draft/datetime.h create mode 100644 util/draft/datetime_ut.cpp create mode 100644 util/draft/enum.cpp create mode 100644 util/draft/enum.h create mode 100644 util/draft/holder_vector.cpp create mode 100644 util/draft/holder_vector.h create mode 100644 util/draft/holder_vector_ut.cpp create mode 100644 util/draft/ip.cpp create mode 100644 util/draft/ip.h create mode 100644 util/draft/matrix.cpp create mode 100644 util/draft/matrix.h create mode 100644 util/draft/memory.cpp create mode 100644 util/draft/memory.h create mode 100644 util/draft/memory_ut.cpp create mode 100644 util/draft/ut/ya.make create mode 100644 util/draft/ya.make create mode 100644 util/folder/dirent_win.c create mode 100644 util/folder/dirent_win.h create mode 100644 util/folder/dirut.cpp create mode 100644 util/folder/dirut.h create mode 100644 util/folder/dirut_ut.cpp create mode 100644 util/folder/filelist.cpp create mode 100644 util/folder/filelist.h create mode 100644 util/folder/filelist_ut.cpp create mode 100644 util/folder/fts.cpp create mode 100644 util/folder/fts.h create mode 100644 util/folder/fts_ut.cpp create mode 100644 util/folder/fwd.cpp create mode 100644 util/folder/fwd.h create mode 100644 util/folder/iterator.cpp create mode 100644 util/folder/iterator.h create mode 100644 util/folder/iterator_ut.cpp create mode 100644 util/folder/lstat_win.c create mode 100644 util/folder/lstat_win.h create mode 100644 util/folder/path.cpp create mode 100644 util/folder/path.h create mode 100644 util/folder/path.pxd create mode 100644 util/folder/path_ut.cpp create mode 100644 util/folder/path_ut.pyx create mode 100644 util/folder/pathsplit.cpp create mode 100644 util/folder/pathsplit.h create mode 100644 util/folder/pathsplit_ut.cpp create mode 100644 util/folder/tempdir.cpp create mode 100644 util/folder/tempdir.h create mode 100644 util/folder/ut/ya.make create mode 100644 util/folder/ut_cython/test_folder.py create mode 100644 util/folder/ut_cython/ya.make create mode 100644 util/folder/ya.make create mode 100644 util/generic/adaptor.cpp create mode 100644 util/generic/adaptor.h create mode 100644 util/generic/adaptor_ut.cpp create mode 100644 util/generic/algorithm.cpp create mode 100644 util/generic/algorithm.h create mode 100644 util/generic/algorithm_ut.cpp create mode 100644 util/generic/array_ref.cpp create mode 100644 util/generic/array_ref.h create mode 100644 util/generic/array_ref.pxd create mode 100644 util/generic/array_ref_ut.cpp create mode 100644 util/generic/array_ref_ut.pyx create mode 100644 util/generic/array_size.cpp create mode 100644 util/generic/array_size.h create mode 100644 util/generic/array_size_ut.cpp create mode 100644 util/generic/bitmap.cpp create mode 100644 util/generic/bitmap.h create mode 100644 util/generic/bitmap_ut.cpp create mode 100644 util/generic/bitops.cpp create mode 100644 util/generic/bitops.h create mode 100644 util/generic/bitops_ut.cpp create mode 100644 util/generic/bt_exception.cpp create mode 100644 util/generic/bt_exception.h create mode 100644 util/generic/buffer.cpp create mode 100644 util/generic/buffer.h create mode 100644 util/generic/buffer_ut.cpp create mode 100644 util/generic/cast.cpp create mode 100644 util/generic/cast.h create mode 100644 util/generic/cast_ut.cpp create mode 100644 util/generic/deque.cpp create mode 100644 util/generic/deque.h create mode 100644 util/generic/deque.pxd create mode 100644 util/generic/deque_ut.cpp create mode 100644 util/generic/deque_ut.pyx create mode 100644 util/generic/enum_range.cpp create mode 100644 util/generic/enum_range.h create mode 100644 util/generic/enum_range_ut.cpp create mode 100644 util/generic/explicit_type.cpp create mode 100644 util/generic/explicit_type.h create mode 100644 util/generic/explicit_type_ut.cpp create mode 100644 util/generic/fastqueue.cpp create mode 100644 util/generic/fastqueue.h create mode 100644 util/generic/flags.cpp create mode 100644 util/generic/flags.h create mode 100644 util/generic/flags_ut.cpp create mode 100644 util/generic/function.cpp create mode 100644 util/generic/function.h create mode 100644 util/generic/function_ref.cpp create mode 100644 util/generic/function_ref.h create mode 100644 util/generic/function_ref_ut.cpp create mode 100644 util/generic/function_ut.cpp create mode 100644 util/generic/fwd.cpp create mode 100644 util/generic/fwd.h create mode 100644 util/generic/guid.cpp create mode 100644 util/generic/guid.h create mode 100644 util/generic/guid_ut.cpp create mode 100644 util/generic/hash.cpp create mode 100644 util/generic/hash.h create mode 100644 util/generic/hash.pxd create mode 100644 util/generic/hash_multi_map.cpp create mode 100644 util/generic/hash_multi_map.h create mode 100644 util/generic/hash_primes.cpp create mode 100644 util/generic/hash_primes.h create mode 100644 util/generic/hash_primes_ut.cpp create mode 100644 util/generic/hash_set.cpp create mode 100644 util/generic/hash_set.h create mode 100644 util/generic/hash_set.pxd create mode 100644 util/generic/hash_set_ut.pyx create mode 100644 util/generic/hash_table.cpp create mode 100644 util/generic/hash_table.h create mode 100644 util/generic/hash_ut.cpp create mode 100644 util/generic/hash_ut.pyx create mode 100644 util/generic/hide_ptr.cpp create mode 100644 util/generic/hide_ptr.h create mode 100644 util/generic/intrlist.cpp create mode 100644 util/generic/intrlist.h create mode 100644 util/generic/intrlist_ut.cpp create mode 100644 util/generic/is_in.cpp create mode 100644 util/generic/is_in.h create mode 100644 util/generic/is_in_ut.cpp create mode 100644 util/generic/iterator.cpp create mode 100644 util/generic/iterator.h create mode 100644 util/generic/iterator_range.cpp create mode 100644 util/generic/iterator_range.h create mode 100644 util/generic/iterator_range_ut.cpp create mode 100644 util/generic/iterator_ut.cpp create mode 100644 util/generic/lazy_value.cpp create mode 100644 util/generic/lazy_value.h create mode 100644 util/generic/lazy_value_ut.cpp create mode 100644 util/generic/list.cpp create mode 100644 util/generic/list.h create mode 100644 util/generic/list.pxd create mode 100644 util/generic/list_ut.cpp create mode 100644 util/generic/list_ut.pyx create mode 100644 util/generic/map.cpp create mode 100644 util/generic/map.h create mode 100644 util/generic/map.pxd create mode 100644 util/generic/map_ut.cpp create mode 100644 util/generic/map_ut.pyx create mode 100644 util/generic/mapfindptr.cpp create mode 100644 util/generic/mapfindptr.h create mode 100644 util/generic/mapfindptr_ut.cpp create mode 100644 util/generic/maybe.cpp create mode 100644 util/generic/maybe.h create mode 100644 util/generic/maybe.pxd create mode 100644 util/generic/maybe_traits.h create mode 100644 util/generic/maybe_ut.cpp create mode 100644 util/generic/maybe_ut.pyx create mode 100644 util/generic/mem_copy.cpp create mode 100644 util/generic/mem_copy.h create mode 100644 util/generic/mem_copy_ut.cpp create mode 100644 util/generic/noncopyable.cpp create mode 100644 util/generic/noncopyable.h create mode 100644 util/generic/object_counter.cpp create mode 100644 util/generic/object_counter.h create mode 100644 util/generic/objects_counter_ut.cpp create mode 100644 util/generic/overloaded.cpp create mode 100644 util/generic/overloaded.h create mode 100644 util/generic/overloaded_ut.cpp create mode 100644 util/generic/ptr.cpp create mode 100644 util/generic/ptr.h create mode 100644 util/generic/ptr.pxd create mode 100644 util/generic/ptr_ut.cpp create mode 100644 util/generic/ptr_ut.pyx create mode 100644 util/generic/queue.cpp create mode 100644 util/generic/queue.h create mode 100644 util/generic/queue_ut.cpp create mode 100644 util/generic/refcount.cpp create mode 100644 util/generic/refcount.h create mode 100644 util/generic/reserve.h create mode 100644 util/generic/scope.cpp create mode 100644 util/generic/scope.h create mode 100644 util/generic/scope_ut.cpp rename {tools/enum_parser/enum_serialization_runtime => util/generic}/serialized_enum.cpp (100%) rename {tools/enum_parser/enum_serialization_runtime => util/generic}/serialized_enum.h (92%) rename {tools/enum_parser/enum_serialization_runtime => util/generic}/serialized_enum_ut.cpp (82%) create mode 100644 util/generic/set.cpp create mode 100644 util/generic/set.h create mode 100644 util/generic/set_ut.cpp create mode 100644 util/generic/singleton.cpp create mode 100644 util/generic/singleton.h create mode 100644 util/generic/singleton_ut.cpp create mode 100644 util/generic/size_literals.cpp create mode 100644 util/generic/size_literals.h create mode 100644 util/generic/size_literals_ut.cpp create mode 100644 util/generic/stack.cpp create mode 100644 util/generic/stack.h create mode 100644 util/generic/stack_ut.cpp create mode 100644 util/generic/store_policy.cpp create mode 100644 util/generic/store_policy.h create mode 100644 util/generic/store_policy_ut.cpp create mode 100644 util/generic/strbase.h create mode 100644 util/generic/strbuf.cpp create mode 100644 util/generic/strbuf.h create mode 100644 util/generic/strbuf_ut.cpp create mode 100644 util/generic/strfcpy.cpp create mode 100644 util/generic/strfcpy.h create mode 100644 util/generic/string.cpp create mode 100644 util/generic/string.h create mode 100644 util/generic/string.pxd create mode 100644 util/generic/string_hash.h create mode 100644 util/generic/string_transparent_hash_ut.cpp create mode 100644 util/generic/string_ut.cpp create mode 100644 util/generic/string_ut.h create mode 100644 util/generic/string_ut.pyx create mode 100644 util/generic/typelist.cpp create mode 100644 util/generic/typelist.h create mode 100644 util/generic/typelist_ut.cpp create mode 100644 util/generic/typetraits.cpp create mode 100644 util/generic/typetraits.h create mode 100644 util/generic/typetraits_ut.cpp create mode 100644 util/generic/ut/ya.make create mode 100644 util/generic/ut_cython/test_generic.py create mode 100644 util/generic/ut_cython/ya.make create mode 100644 util/generic/utility.cpp create mode 100644 util/generic/utility.h create mode 100644 util/generic/utility_ut.cpp create mode 100644 util/generic/va_args.cpp create mode 100644 util/generic/va_args.h create mode 100644 util/generic/va_args_ut.cpp create mode 100644 util/generic/variant.cpp create mode 100644 util/generic/variant.h create mode 100644 util/generic/vector.cpp create mode 100644 util/generic/vector.h create mode 100644 util/generic/vector.pxd create mode 100644 util/generic/vector_ut.cpp create mode 100644 util/generic/vector_ut.pyx create mode 100644 util/generic/xrange.cpp create mode 100644 util/generic/xrange.h create mode 100644 util/generic/xrange_ut.cpp create mode 100644 util/generic/ya.make create mode 100644 util/generic/yexception.cpp create mode 100644 util/generic/yexception.h create mode 100644 util/generic/yexception_ut.c create mode 100644 util/generic/yexception_ut.cpp create mode 100644 util/generic/yexception_ut.h create mode 100644 util/generic/ylimits.cpp create mode 100644 util/generic/ylimits.h create mode 100644 util/generic/ylimits_ut.cpp create mode 100644 util/generic/ymath.cpp create mode 100644 util/generic/ymath.h create mode 100644 util/generic/ymath_ut.cpp create mode 100644 util/memory/addstorage.cpp create mode 100644 util/memory/addstorage.h create mode 100644 util/memory/addstorage_ut.cpp create mode 100644 util/memory/alloc.cpp create mode 100644 util/memory/alloc.h create mode 100644 util/memory/blob.cpp create mode 100644 util/memory/blob.h create mode 100644 util/memory/blob.pxd create mode 100644 util/memory/blob_ut.cpp create mode 100644 util/memory/blob_ut.pyx create mode 100644 util/memory/mmapalloc.cpp create mode 100644 util/memory/mmapalloc.h create mode 100644 util/memory/pool.cpp create mode 100644 util/memory/pool.h create mode 100644 util/memory/pool_ut.cpp create mode 100644 util/memory/segmented_string_pool.cpp create mode 100644 util/memory/segmented_string_pool.h create mode 100644 util/memory/segpool_alloc.cpp create mode 100644 util/memory/segpool_alloc.h create mode 100644 util/memory/smallobj.cpp create mode 100644 util/memory/smallobj.h create mode 100644 util/memory/smallobj_ut.cpp create mode 100644 util/memory/tempbuf.cpp create mode 100644 util/memory/tempbuf.h create mode 100644 util/memory/tempbuf_ut.cpp create mode 100644 util/memory/ut/ya.make create mode 100644 util/memory/ut_cython/test_memory.py create mode 100644 util/memory/ut_cython/ya.make create mode 100644 util/memory/ya.make create mode 100644 util/network/address.cpp create mode 100644 util/network/address.h create mode 100644 util/network/address_ut.cpp create mode 100644 util/network/endpoint.cpp create mode 100644 util/network/endpoint.h create mode 100644 util/network/endpoint_ut.cpp create mode 100644 util/network/hostip.cpp create mode 100644 util/network/hostip.h create mode 100644 util/network/init.cpp create mode 100644 util/network/init.h create mode 100644 util/network/interface.cpp create mode 100644 util/network/interface.h create mode 100644 util/network/iovec.cpp create mode 100644 util/network/iovec.h create mode 100644 util/network/ip.cpp create mode 100644 util/network/ip.h create mode 100644 util/network/ip_ut.cpp create mode 100644 util/network/nonblock.cpp create mode 100644 util/network/nonblock.h create mode 100644 util/network/pair.cpp create mode 100644 util/network/pair.h create mode 100644 util/network/poller.cpp create mode 100644 util/network/poller.h create mode 100644 util/network/poller_ut.cpp create mode 100644 util/network/pollerimpl.cpp create mode 100644 util/network/pollerimpl.h create mode 100644 util/network/sock.cpp create mode 100644 util/network/sock.h create mode 100644 util/network/sock_ut.cpp create mode 100644 util/network/socket.cpp create mode 100644 util/network/socket.h create mode 100644 util/network/socket_ut.cpp create mode 100644 util/network/ut/ya.make create mode 100644 util/network/ya.make create mode 100644 util/random/common_ops.cpp create mode 100644 util/random/common_ops.h create mode 100644 util/random/common_ops_ut.cpp create mode 100644 util/random/easy.cpp create mode 100644 util/random/easy.h create mode 100644 util/random/easy_ut.cpp create mode 100644 util/random/entropy.cpp create mode 100644 util/random/entropy.h create mode 100644 util/random/entropy_ut.cpp create mode 100644 util/random/fast.cpp create mode 100644 util/random/fast.h create mode 100644 util/random/fast_ut.cpp create mode 100644 util/random/init_atfork.cpp create mode 100644 util/random/init_atfork.h create mode 100644 util/random/lcg_engine.cpp create mode 100644 util/random/lcg_engine.h create mode 100644 util/random/mersenne.cpp create mode 100644 util/random/mersenne.h create mode 100644 util/random/mersenne32.cpp create mode 100644 util/random/mersenne32.h create mode 100644 util/random/mersenne64.cpp create mode 100644 util/random/mersenne64.h create mode 100644 util/random/mersenne_ut.cpp create mode 100644 util/random/normal.cpp create mode 100644 util/random/normal.h create mode 100644 util/random/normal_ut.cpp create mode 100644 util/random/random.cpp create mode 100644 util/random/random.h create mode 100644 util/random/random_ut.cpp create mode 100644 util/random/shuffle.cpp create mode 100644 util/random/shuffle.h create mode 100644 util/random/shuffle_ut.cpp create mode 100644 util/random/ut/ya.make create mode 100644 util/random/ya.make create mode 100644 util/str_stl.cpp create mode 100644 util/str_stl.h create mode 100644 util/stream/aligned.cpp create mode 100644 util/stream/aligned.h create mode 100644 util/stream/aligned_ut.cpp create mode 100644 util/stream/buffer.cpp create mode 100644 util/stream/buffer.h create mode 100644 util/stream/buffer_ut.cpp create mode 100644 util/stream/buffered.cpp create mode 100644 util/stream/buffered.h create mode 100644 util/stream/buffered_ut.cpp create mode 100644 util/stream/debug.cpp create mode 100644 util/stream/debug.h create mode 100644 util/stream/direct_io.cpp create mode 100644 util/stream/direct_io.h create mode 100644 util/stream/direct_io_ut.cpp create mode 100644 util/stream/file.cpp create mode 100644 util/stream/file.h create mode 100644 util/stream/file_ut.cpp create mode 100644 util/stream/format.cpp create mode 100644 util/stream/format.h create mode 100644 util/stream/format_std_ut.cpp create mode 100644 util/stream/format_ut.cpp create mode 100644 util/stream/fwd.cpp create mode 100644 util/stream/fwd.h create mode 100644 util/stream/hex.cpp create mode 100644 util/stream/hex.h create mode 100644 util/stream/hex_ut.cpp create mode 100644 util/stream/holder.cpp create mode 100644 util/stream/holder.h create mode 100644 util/stream/input.cpp create mode 100644 util/stream/input.h create mode 100644 util/stream/input_ut.cpp create mode 100644 util/stream/ios_ut.cpp create mode 100644 util/stream/labeled.cpp create mode 100644 util/stream/labeled.h create mode 100644 util/stream/labeled_ut.cpp create mode 100644 util/stream/length.cpp create mode 100644 util/stream/length.h create mode 100644 util/stream/length_ut.cpp create mode 100644 util/stream/mem.cpp create mode 100644 util/stream/mem.h create mode 100644 util/stream/mem_ut.cpp create mode 100644 util/stream/multi.cpp create mode 100644 util/stream/multi.h create mode 100644 util/stream/multi_ut.cpp create mode 100644 util/stream/null.cpp create mode 100644 util/stream/null.h create mode 100644 util/stream/output.cpp create mode 100644 util/stream/output.h create mode 100644 util/stream/output.pxd create mode 100644 util/stream/pipe.cpp create mode 100644 util/stream/pipe.h create mode 100644 util/stream/printf.cpp create mode 100644 util/stream/printf.h create mode 100644 util/stream/printf_ut.cpp create mode 100644 util/stream/str.cpp create mode 100644 util/stream/str.h create mode 100644 util/stream/str.pxd create mode 100644 util/stream/str_ut.cpp create mode 100644 util/stream/str_ut.pyx create mode 100644 util/stream/tee.cpp create mode 100644 util/stream/tee.h create mode 100644 util/stream/tempbuf.cpp create mode 100644 util/stream/tempbuf.h create mode 100644 util/stream/tokenizer.cpp create mode 100644 util/stream/tokenizer.h create mode 100644 util/stream/tokenizer_ut.cpp create mode 100644 util/stream/trace.cpp create mode 100644 util/stream/trace.h create mode 100644 util/stream/ut/ya.make create mode 100644 util/stream/ut_cython/test_stream.py create mode 100644 util/stream/ut_cython/ya.make create mode 100644 util/stream/walk.cpp create mode 100644 util/stream/walk.h create mode 100644 util/stream/walk_ut.cpp create mode 100644 util/stream/ya.make create mode 100644 util/stream/zerocopy.cpp create mode 100644 util/stream/zerocopy.h create mode 100644 util/stream/zerocopy_output.cpp create mode 100644 util/stream/zerocopy_output.h create mode 100644 util/stream/zerocopy_output_ut.cpp create mode 100644 util/stream/zlib.cpp create mode 100644 util/stream/zlib.h create mode 100644 util/stream/zlib_ut.cpp create mode 100644 util/string/ascii.cpp create mode 100644 util/string/ascii.h create mode 100644 util/string/ascii_ut.cpp create mode 100644 util/string/builder.cpp create mode 100644 util/string/builder.h create mode 100644 util/string/builder_ut.cpp create mode 100644 util/string/cast.cpp create mode 100644 util/string/cast.h create mode 100644 util/string/cast.pxd create mode 100644 util/string/cast_ut.cpp create mode 100644 util/string/cast_ut.pyx create mode 100644 util/string/cstriter.cpp create mode 100644 util/string/cstriter.h create mode 100644 util/string/escape.cpp create mode 100644 util/string/escape.h create mode 100644 util/string/escape_ut.cpp create mode 100644 util/string/hex.cpp create mode 100644 util/string/hex.h create mode 100644 util/string/hex_ut.cpp create mode 100644 util/string/join.cpp create mode 100644 util/string/join.h create mode 100644 util/string/join_ut.cpp create mode 100644 util/string/printf.cpp create mode 100644 util/string/printf.h create mode 100644 util/string/printf_ut.cpp create mode 100644 util/string/reverse.cpp create mode 100644 util/string/reverse.h create mode 100644 util/string/split.cpp create mode 100644 util/string/split.h create mode 100644 util/string/split_ut.cpp create mode 100644 util/string/strip.cpp create mode 100644 util/string/strip.h create mode 100644 util/string/strip_ut.cpp create mode 100644 util/string/strspn.cpp create mode 100644 util/string/strspn.h create mode 100644 util/string/strspn_ut.cpp create mode 100644 util/string/subst.cpp create mode 100644 util/string/subst.h create mode 100644 util/string/subst_ut.cpp create mode 100644 util/string/type.cpp create mode 100644 util/string/type.h create mode 100644 util/string/type_ut.cpp create mode 100644 util/string/ut/ya.make create mode 100644 util/string/ut_cython/test_string.py create mode 100644 util/string/ut_cython/ya.make create mode 100644 util/string/util.cpp create mode 100644 util/string/util.h create mode 100644 util/string/util_ut.cpp create mode 100644 util/string/vector.cpp create mode 100644 util/string/vector.h create mode 100644 util/string/vector_ut.cpp create mode 100644 util/string/ya.make create mode 100644 util/system/align.cpp create mode 100644 util/system/align.h create mode 100644 util/system/align_ut.cpp create mode 100644 util/system/atexit.cpp create mode 100644 util/system/atexit.h create mode 100644 util/system/atexit_ut.cpp create mode 100644 util/system/backtrace.cpp create mode 100644 util/system/backtrace.h create mode 100644 util/system/backtrace_ut.cpp create mode 100644 util/system/byteorder.cpp create mode 100644 util/system/byteorder.h create mode 100644 util/system/byteorder_ut.cpp create mode 100644 util/system/compat.cpp create mode 100644 util/system/compat.h create mode 100644 util/system/compat_ut.cpp create mode 100644 util/system/compiler.cpp create mode 100644 util/system/compiler.h create mode 100644 util/system/compiler_ut.cpp create mode 100644 util/system/condvar.cpp create mode 100644 util/system/condvar.h create mode 100644 util/system/condvar_ut.cpp create mode 100644 util/system/context.cpp create mode 100644 util/system/context.h create mode 100644 util/system/context_aarch64.S create mode 100644 util/system/context_aarch64.h create mode 100644 util/system/context_i686.asm create mode 100644 util/system/context_i686.h create mode 100644 util/system/context_ut.cpp create mode 100644 util/system/context_x86.asm create mode 100644 util/system/context_x86.h create mode 100644 util/system/context_x86_64.asm create mode 100644 util/system/context_x86_64.h create mode 100644 util/system/cpu_id.cpp create mode 100644 util/system/cpu_id.h create mode 100644 util/system/cpu_id_ut.cpp create mode 100644 util/system/daemon.cpp create mode 100644 util/system/daemon.h create mode 100644 util/system/daemon_ut.cpp create mode 100644 util/system/datetime.cpp create mode 100644 util/system/datetime.h create mode 100644 util/system/datetime_ut.cpp create mode 100644 util/system/defaults.c create mode 100644 util/system/defaults.h create mode 100644 util/system/demangle_impl.h create mode 100644 util/system/direct_io.cpp create mode 100644 util/system/direct_io.h create mode 100644 util/system/direct_io_ut.cpp create mode 100644 util/system/dynlib.cpp create mode 100644 util/system/dynlib.h create mode 100644 util/system/env.cpp create mode 100644 util/system/env.h create mode 100644 util/system/env_ut.cpp create mode 100644 util/system/err.cpp create mode 100644 util/system/error.cpp create mode 100644 util/system/error.h create mode 100644 util/system/error_ut.cpp create mode 100644 util/system/event.cpp create mode 100644 util/system/event.h create mode 100644 util/system/event_ut.cpp create mode 100644 util/system/execpath.cpp create mode 100644 util/system/execpath.h create mode 100644 util/system/execpath_ut.cpp create mode 100644 util/system/fasttime.cpp create mode 100644 util/system/fasttime.h create mode 100644 util/system/fhandle.cpp create mode 100644 util/system/fhandle.h create mode 100644 util/system/file.cpp create mode 100644 util/system/file.h create mode 100644 util/system/file_lock.cpp create mode 100644 util/system/file_lock.h create mode 100644 util/system/file_ut.cpp create mode 100644 util/system/filemap.cpp create mode 100644 util/system/filemap.h create mode 100644 util/system/filemap_ut.cpp create mode 100644 util/system/flock.cpp create mode 100644 util/system/flock.h create mode 100644 util/system/flock_ut.cpp create mode 100644 util/system/fs.cpp create mode 100644 util/system/fs.h create mode 100644 util/system/fs_ut.cpp create mode 100644 util/system/fs_win.cpp create mode 100644 util/system/fs_win.h create mode 100644 util/system/fs_win_ut.cpp create mode 100644 util/system/fstat.cpp create mode 100644 util/system/fstat.h create mode 100644 util/system/fstat_ut.cpp create mode 100644 util/system/getpid.cpp create mode 100644 util/system/getpid.h create mode 100644 util/system/getpid_ut.cpp create mode 100644 util/system/guard.cpp create mode 100644 util/system/guard.h create mode 100644 util/system/guard_ut.cpp create mode 100644 util/system/hi_lo.cpp create mode 100644 util/system/hi_lo.h create mode 100644 util/system/hi_lo_ut.cpp create mode 100644 util/system/hostname.cpp create mode 100644 util/system/hostname.h create mode 100644 util/system/hostname_ut.cpp create mode 100644 util/system/hp_timer.cpp create mode 100644 util/system/hp_timer.h create mode 100644 util/system/info.cpp create mode 100644 util/system/info.h create mode 100644 util/system/info_ut.cpp create mode 100644 util/system/interrupt_signals.cpp create mode 100644 util/system/interrupt_signals.h create mode 100644 util/system/interrupt_signals_ut.cpp create mode 100644 util/system/madvise.cpp create mode 100644 util/system/madvise.h create mode 100644 util/system/maxlen.cpp create mode 100644 util/system/maxlen.h create mode 100644 util/system/mem_info.cpp create mode 100644 util/system/mem_info.h create mode 100644 util/system/mem_info_ut.cpp create mode 100644 util/system/mincore.cpp create mode 100644 util/system/mincore.h create mode 100644 util/system/mincore_ut.cpp create mode 100644 util/system/mktemp.cpp create mode 100644 util/system/mktemp_system.cpp create mode 100644 util/system/mktemp_ut.cpp create mode 100644 util/system/mlock.cpp create mode 100644 util/system/mlock.h create mode 100644 util/system/mutex.cpp create mode 100644 util/system/mutex.h create mode 100644 util/system/mutex_ut.cpp create mode 100644 util/system/nice.cpp create mode 100644 util/system/nice.h create mode 100644 util/system/nice_ut.cpp create mode 100644 util/system/pipe.cpp create mode 100644 util/system/pipe.h create mode 100644 util/system/pipe_ut.cpp create mode 100644 util/system/platform.cpp create mode 100644 util/system/platform.h create mode 100644 util/system/platform_ut.cpp create mode 100644 util/system/progname.cpp create mode 100644 util/system/progname.h create mode 100644 util/system/progname_ut.cpp create mode 100644 util/system/protect.cpp create mode 100644 util/system/protect.h create mode 100644 util/system/rusage.cpp create mode 100644 util/system/rusage.h create mode 100644 util/system/rusage_ut.cpp create mode 100644 util/system/rwlock.cpp create mode 100644 util/system/rwlock.h create mode 100644 util/system/rwlock_ut.cpp create mode 100644 util/system/sanitizers.cpp create mode 100644 util/system/sanitizers.h create mode 100644 util/system/sanitizers_ut.cpp create mode 100644 util/system/sem.cpp create mode 100644 util/system/sem.h create mode 100644 util/system/shellcommand.cpp create mode 100644 util/system/shellcommand.h create mode 100644 util/system/shellcommand_ut.cpp create mode 100644 util/system/shmat.cpp create mode 100644 util/system/shmat.h create mode 100644 util/system/shmat_ut.cpp create mode 100644 util/system/sigset.cpp create mode 100644 util/system/sigset.h create mode 100644 util/system/spin_wait.cpp create mode 100644 util/system/spin_wait.h create mode 100644 util/system/spinlock.cpp create mode 100644 util/system/spinlock.h create mode 100644 util/system/spinlock_ut.cpp create mode 100644 util/system/src_location.cpp create mode 100644 util/system/src_location.h create mode 100644 util/system/src_location_ut.cpp create mode 100644 util/system/src_root.h create mode 100644 util/system/src_root_ut.cpp create mode 100644 util/system/sys_alloc.cpp create mode 100644 util/system/sys_alloc.h create mode 100644 util/system/sysstat.cpp create mode 100644 util/system/sysstat.h create mode 100644 util/system/tempfile.cpp create mode 100644 util/system/tempfile.h create mode 100644 util/system/tempfile_ut.cpp create mode 100644 util/system/thread.cpp create mode 100644 util/system/thread.h create mode 100644 util/system/thread.i create mode 100644 util/system/thread_ut.cpp create mode 100644 util/system/tls.cpp create mode 100644 util/system/tls.h create mode 100644 util/system/tls_ut.cpp create mode 100644 util/system/type_name.cpp create mode 100644 util/system/type_name.h create mode 100644 util/system/type_name_ut.cpp create mode 100644 util/system/types.cpp create mode 100644 util/system/types.h create mode 100644 util/system/types.pxd create mode 100644 util/system/types_ut.cpp create mode 100644 util/system/types_ut.pyx create mode 100644 util/system/unaligned_mem.cpp create mode 100644 util/system/unaligned_mem.h create mode 100644 util/system/unaligned_mem_ut.cpp create mode 100644 util/system/user.cpp create mode 100644 util/system/user.h create mode 100644 util/system/user_ut.cpp create mode 100644 util/system/ut/stdin_osfhandle/main.cpp create mode 100644 util/system/ut/stdin_osfhandle/ya.make create mode 100644 util/system/ut/ya.make create mode 100644 util/system/ut_cython/test_system.py create mode 100644 util/system/ut_cython/ya.make create mode 100644 util/system/utime.cpp create mode 100644 util/system/utime.h create mode 100644 util/system/valgrind.cpp create mode 100644 util/system/valgrind.h create mode 100644 util/system/win_undef.h create mode 100644 util/system/winint.cpp create mode 100644 util/system/winint.h create mode 100644 util/system/ya.make create mode 100644 util/system/yassert.cpp create mode 100644 util/system/yassert.h create mode 100644 util/system/yassert_ut.cpp create mode 100644 util/system/yield.cpp create mode 100644 util/system/yield.h create mode 100644 util/tests/ya_util_tests.inc create mode 100644 util/thread/factory.cpp create mode 100644 util/thread/factory.h create mode 100644 util/thread/factory_ut.cpp create mode 100644 util/thread/fwd.cpp create mode 100644 util/thread/fwd.h create mode 100644 util/thread/lfqueue.cpp create mode 100644 util/thread/lfqueue.h create mode 100644 util/thread/lfqueue_ut.cpp create mode 100644 util/thread/lfstack.cpp create mode 100644 util/thread/lfstack.h create mode 100644 util/thread/lfstack_ut.cpp create mode 100644 util/thread/pool.cpp create mode 100644 util/thread/pool.h create mode 100644 util/thread/pool_ut.cpp create mode 100644 util/thread/singleton.cpp create mode 100644 util/thread/singleton.h create mode 100644 util/thread/singleton_ut.cpp create mode 100644 util/thread/ut/ya.make create mode 100644 util/thread/ya.make create mode 100644 util/ut/ya.make create mode 100644 util/ya.make create mode 100644 util/ysafeptr.cpp create mode 100644 util/ysafeptr.h create mode 100644 util/ysaveload.cpp create mode 100644 util/ysaveload.h create mode 100644 util/ysaveload_ut.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 152a6866e5e..8601f472f50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ include(cmake/external_libs.cmake) add_subdirectory(tools) add_subdirectory(contrib) add_subdirectory(src) +add_subdirectory(util) if (YDB_SDK_EXAMPLES) add_subdirectory(examples) diff --git a/cmake/public_headers.txt b/cmake/public_headers.txt new file mode 100644 index 00000000000..95b851f3c28 --- /dev/null +++ b/cmake/public_headers.txt @@ -0,0 +1,84 @@ +util/datetime/base.h +util/datetime/systime.h +util/digest/multi.h +util/digest/numeric.h +util/digest/sequence.h +util/generic/algorithm.h +util/generic/array_size.h +util/generic/bt_exception.h +util/generic/cast.h +util/generic/flags.h +util/generic/function.h +util/generic/fwd.h +util/generic/is_in.h +util/generic/iterator_range.h +util/generic/noncopyable.h +util/generic/ptr.h +util/generic/refcount.h +util/generic/singleton.h +util/generic/size_literals.h +util/generic/store_policy.h +util/generic/string_hash.h +util/generic/typelist.h +util/generic/typetraits.h +util/generic/utility.h +util/generic/va_args.h +util/generic/xrange.h +util/generic/yexception.h +util/generic/ylimits.h +util/memory/alloc.h +util/memory/blob.h +util/memory/tempbuf.h +util/network/address.h +util/network/hostip.h +util/network/init.h +util/network/ip.h +util/network/sock.h +util/network/socket.h +util/random/random.h +util/stream/debug.h +util/stream/fwd.h +util/stream/input.h +util/stream/mem.h +util/stream/output.h +util/stream/str.h +util/stream/tempbuf.h +util/stream/zerocopy_output.h +util/stream/zerocopy.h +util/string/ascii.h +util/string/builder.h +util/string/cast.h +util/string/escape.h +util/string/strip.h +util/string/subst.h +util/system/atexit.h +util/system/backtrace.h +util/system/byteorder.h +util/system/compat.h +util/system/compiler.h +util/system/defaults.h +util/system/error.h +util/system/event.h +util/system/fhandle.h +util/system/file.h +util/system/flock.h +util/system/guard.h +util/system/maxlen.h +util/system/platform.h +util/system/progname.h +util/system/rwlock.h +util/system/spin_wait.h +util/system/spinlock.h +util/system/src_location.h +util/system/src_root.h +util/system/sysstat.h +util/system/type_name.h +util/system/types.h +util/system/unaligned_mem.h +util/system/win_undef.h +util/system/winint.h +util/system/yassert.h +util/thread/factory.h +util/thread/pool.h +util/str_stl.h +util/ysaveload.h \ No newline at end of file diff --git a/contrib/libs/CMakeLists.txt b/contrib/libs/CMakeLists.txt index b7e67d54e1d..3edb4119484 100644 --- a/contrib/libs/CMakeLists.txt +++ b/contrib/libs/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(libc_compat) add_subdirectory(lzmasdk) diff --git a/contrib/libs/libc_compat/CMakeLists.txt b/contrib/libs/libc_compat/CMakeLists.txt new file mode 100644 index 00000000000..f47a6b570e1 --- /dev/null +++ b/contrib/libs/libc_compat/CMakeLists.txt @@ -0,0 +1,23 @@ +add_library(contrib-libs-libc_compat) + +target_compile_options(contrib-libs-libc_compat PRIVATE + $,,-Wno-everything> +) + +target_include_directories(contrib-libs-libc_compat PUBLIC + ${YDB_SDK_SOURCE_DIR}/contrib/libs/libc_compat/include/readpassphrase + ${YDB_SDK_SOURCE_DIR}/contrib/libs/libc_compat/reallocarray + ${YDB_SDK_SOURCE_DIR}/contrib/libs/libc_compat/random +) + +target_sources(contrib-libs-libc_compat PRIVATE + ${YDB_SDK_SOURCE_DIR}/contrib/libs/libc_compat/string.c + ${YDB_SDK_SOURCE_DIR}/contrib/libs/libc_compat/readpassphrase.c + ${YDB_SDK_SOURCE_DIR}/contrib/libs/libc_compat/explicit_bzero.c + ${YDB_SDK_SOURCE_DIR}/contrib/libs/libc_compat/memfd_create.c + ${YDB_SDK_SOURCE_DIR}/contrib/libs/libc_compat/strlcat.c + ${YDB_SDK_SOURCE_DIR}/contrib/libs/libc_compat/strlcpy.c + ${YDB_SDK_SOURCE_DIR}/contrib/libs/libc_compat/reallocarray/reallocarray.c + ${YDB_SDK_SOURCE_DIR}/contrib/libs/libc_compat/random/getrandom.c + ${YDB_SDK_SOURCE_DIR}/contrib/libs/libc_compat/random/getentropy.c +) diff --git a/contrib/libs/libc_compat/README.md b/contrib/libs/libc_compat/README.md new file mode 100644 index 00000000000..fe7a22fbb1e --- /dev/null +++ b/contrib/libs/libc_compat/README.md @@ -0,0 +1,24 @@ +This library implements a compatibility layer between various libc implementations. + +The rationale for the library implementation is described in https://st.yandex-team.ru/IGNIETFERRO-1439. + +The code is taken from multiple sources, thus both LICENSE() and VERSION() tags are not very representative. + + +During development one can make use of the following mapping of `OS_SDK` into glibc version. + +| Ubuntu | glibc | +| ------ | ----- | +| 20.04 | 2.30 | +| 18.04 | 2.27 | +| 16.04 | 2.23 | +| 14.04 | 2.18 | +| 12.04 | 2.15 | +| 10.04 | 2.11 | + +Use the following commands to update the table above: + +1. `ya make util -DOS_SDK=ubuntu-xx -G | grep OS_SDK_ROOT | head -n 1` +2. `cd ~/.ya/tools/v4/$RESOURCE_ID` +3. `readelf -V $(find . -name 'libc.so.6')` +4. Take the latest version from `.gnu.version_d` section prior to `GLIBC_PRIVATE` diff --git a/contrib/libs/libc_compat/explicit_bzero.c b/contrib/libs/libc_compat/explicit_bzero.c new file mode 100644 index 00000000000..95c27654090 --- /dev/null +++ b/contrib/libs/libc_compat/explicit_bzero.c @@ -0,0 +1,58 @@ +/*------------------------------------------------------------------------- + * + * explicit_bzero.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/explicit_bzero.c + * + *------------------------------------------------------------------------- + */ + +#include +#include + +#if defined(HAVE_MEMSET_S) + +void +explicit_bzero(void *buf, size_t len) +{ + (void) memset_s(buf, len, 0, len); +} + +#elif defined(WIN32) + +#include + +void +explicit_bzero(void *buf, size_t len) +{ + (void) SecureZeroMemory(buf, len); +} + +#else + +/* + * Indirect call through a volatile pointer to hopefully avoid dead-store + * optimisation eliminating the call. (Idea taken from OpenSSH.) We can't + * assume bzero() is present either, so for simplicity we define our own. + */ + +static void +bzero2(void *buf, size_t len) +{ + memset(buf, 0, len); +} + +static void (*volatile bzero_p) (void *, size_t) = bzero2; + +void +explicit_bzero(void *buf, size_t len) +{ + bzero_p(buf, len); +} + +#endif diff --git a/contrib/libs/libc_compat/getservbyname/getservbyname.c b/contrib/libs/libc_compat/getservbyname/getservbyname.c new file mode 100644 index 00000000000..1fff91d0058 --- /dev/null +++ b/contrib/libs/libc_compat/getservbyname/getservbyname.c @@ -0,0 +1,11 @@ +#include + +struct servent *getservbyname(const char *name, const char *prots) +{ + static struct servent se; + static char *buf[2]; + struct servent *res; + if (getservbyname_r(name, prots, &se, (void *)buf, sizeof buf, &res)) + return 0; + return &se; +} diff --git a/contrib/libs/libc_compat/getservbyname/getservbyname_r.c b/contrib/libs/libc_compat/getservbyname/getservbyname_r.c new file mode 100644 index 00000000000..cad6317ab84 --- /dev/null +++ b/contrib/libs/libc_compat/getservbyname/getservbyname_r.c @@ -0,0 +1,55 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include "lookup.h" + +#define ALIGN (sizeof(struct { char a; char *b; }) - sizeof(char *)) + +int getservbyname_r(const char *name, const char *prots, + struct servent *se, char *buf, size_t buflen, struct servent **res) +{ + struct service servs[MAXSERVS]; + int cnt, proto, align; + + *res = 0; + + /* Don't treat numeric port number strings as service records. */ + char *end = ""; + strtoul(name, &end, 10); + if (!*end) return ENOENT; + + /* Align buffer */ + align = -(uintptr_t)buf & ALIGN-1; + if (buflen < 2*sizeof(char *)+align) + return ERANGE; + buf += align; + + if (!prots) proto = 0; + else if (!strcmp(prots, "tcp")) proto = IPPROTO_TCP; + else if (!strcmp(prots, "udp")) proto = IPPROTO_UDP; + else return EINVAL; + + cnt = __lookup_serv(servs, name, proto, 0, 0); + if (cnt<0) switch (cnt) { + case EAI_MEMORY: + case EAI_SYSTEM: + return ENOMEM; + default: + return ENOENT; + } + + se->s_name = (char *)name; + se->s_aliases = (void *)buf; + se->s_aliases[0] = se->s_name; + se->s_aliases[1] = 0; + se->s_port = htons(servs[0].port); + se->s_proto = servs[0].proto == IPPROTO_TCP ? "tcp" : "udp"; + + *res = se; + return 0; +} diff --git a/contrib/libs/libc_compat/getservbyname/lookup.h b/contrib/libs/libc_compat/getservbyname/lookup.h new file mode 100644 index 00000000000..7dc1fb81093 --- /dev/null +++ b/contrib/libs/libc_compat/getservbyname/lookup.h @@ -0,0 +1,57 @@ +#ifndef LOOKUP_H +#define LOOKUP_H + +#include +#include +#include +#include +#include + +#define hidden __attribute__((__visibility__("hidden"))) + +struct aibuf { + struct addrinfo ai; + union sa { + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } sa; + volatile int lock[1]; + short slot, ref; +}; + +struct address { + int family; + unsigned scopeid; + uint8_t addr[16]; + int sortkey; +}; + +struct service { + uint16_t port; + unsigned char proto, socktype; +}; + +#define MAXNS 3 + +struct resolvconf { + struct address ns[MAXNS]; + unsigned nns, attempts, ndots; + unsigned timeout; +}; + +/* The limit of 48 results is a non-sharp bound on the number of addresses + * that can fit in one 512-byte DNS packet full of v4 results and a second + * packet full of v6 results. Due to headers, the actual limit is lower. */ +#define MAXADDRS 48 +#define MAXSERVS 2 + +hidden int __lookup_serv(struct service buf[static MAXSERVS], const char *name, int proto, int socktype, int flags); +hidden int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family, int flags); +hidden int __lookup_ipliteral(struct address buf[static 1], const char *name, int family); + +hidden int __get_resolv_conf(struct resolvconf *, char *, size_t); +hidden int __res_msend_rc(int, const unsigned char *const *, const int *, unsigned char *const *, int *, int, const struct resolvconf *); + +hidden int __dns_parse(const unsigned char *, int, int (*)(void *, int, const void *, int, const void *), void *); + +#endif diff --git a/contrib/libs/libc_compat/getservbyname/lookup_serv.c b/contrib/libs/libc_compat/getservbyname/lookup_serv.c new file mode 100644 index 00000000000..31d407cf28f --- /dev/null +++ b/contrib/libs/libc_compat/getservbyname/lookup_serv.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lookup.h" + +int __lookup_serv(struct service buf[static MAXSERVS], const char *name, int proto, int socktype, int flags) +{ + char line[128]; + int cnt = 0; + char *p, *z = ""; + unsigned long port = 0; + + switch (socktype) { + case SOCK_STREAM: + switch (proto) { + case 0: + proto = IPPROTO_TCP; + case IPPROTO_TCP: + break; + default: + return EAI_SERVICE; + } + break; + case SOCK_DGRAM: + switch (proto) { + case 0: + proto = IPPROTO_UDP; + case IPPROTO_UDP: + break; + default: + return EAI_SERVICE; + } + case 0: + break; + default: + if (name) return EAI_SERVICE; + buf[0].port = 0; + buf[0].proto = proto; + buf[0].socktype = socktype; + return 1; + } + + if (name) { + if (!*name) return EAI_SERVICE; + port = strtoul(name, &z, 10); + } + if (!*z) { + if (port > 65535) return EAI_SERVICE; + if (proto != IPPROTO_UDP) { + buf[cnt].port = port; + buf[cnt].socktype = SOCK_STREAM; + buf[cnt++].proto = IPPROTO_TCP; + } + if (proto != IPPROTO_TCP) { + buf[cnt].port = port; + buf[cnt].socktype = SOCK_DGRAM; + buf[cnt++].proto = IPPROTO_UDP; + } + return cnt; + } + + if (flags & AI_NUMERICSERV) return EAI_NONAME; + + size_t l = strlen(name); + + FILE *f = fopen("/etc/services", "rb"); + if (!f) switch (errno) { + case ENOENT: + case ENOTDIR: + case EACCES: + return EAI_SERVICE; + default: + return EAI_SYSTEM; + } + + while (fgets(line, sizeof line, f) && cnt < MAXSERVS) { + if ((p=strchr(line, '#'))) *p++='\n', *p=0; + + /* Find service name */ + for(p=line; (p=strstr(p, name)); p++) { + if (p>line && !isspace(p[-1])) continue; + if (p[l] && !isspace(p[l])) continue; + break; + } + if (!p) continue; + + /* Skip past canonical name at beginning of line */ + for (p=line; *p && !isspace(*p); p++); + + port = strtoul(p, &z, 10); + if (port > 65535 || z==p) continue; + if (!strncmp(z, "/udp", 4)) { + if (proto == IPPROTO_TCP) continue; + buf[cnt].port = port; + buf[cnt].socktype = SOCK_DGRAM; + buf[cnt++].proto = IPPROTO_UDP; + } + if (!strncmp(z, "/tcp", 4)) { + if (proto == IPPROTO_UDP) continue; + buf[cnt].port = port; + buf[cnt].socktype = SOCK_STREAM; + buf[cnt++].proto = IPPROTO_TCP; + } + } + fclose(f); + return cnt > 0 ? cnt : EAI_SERVICE; +} diff --git a/contrib/libs/libc_compat/include/ifaddrs/ifaddrs.h b/contrib/libs/libc_compat/include/ifaddrs/ifaddrs.h new file mode 100644 index 00000000000..9cd19fec129 --- /dev/null +++ b/contrib/libs/libc_compat/include/ifaddrs/ifaddrs.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1995, 1999 + * Berkeley Software Design, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp + */ + +#ifndef _IFADDRS_H_ +#define _IFADDRS_H_ + +struct ifaddrs { + struct ifaddrs *ifa_next; + char *ifa_name; + unsigned int ifa_flags; + struct sockaddr *ifa_addr; + struct sockaddr *ifa_netmask; + struct sockaddr *ifa_dstaddr; + void *ifa_data; +}; + +/* + * This may have been defined in . Note that if is + * to be included it must be included before this header file. + */ +#ifndef ifa_broadaddr +#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ +#endif + +#include + +__BEGIN_DECLS +extern int getifaddrs(struct ifaddrs **ifap); +extern void freeifaddrs(struct ifaddrs *ifa); +__END_DECLS + +#endif diff --git a/contrib/libs/libc_compat/include/readpassphrase/readpassphrase.h b/contrib/libs/libc_compat/include/readpassphrase/readpassphrase.h new file mode 100644 index 00000000000..ba1ee14ef16 --- /dev/null +++ b/contrib/libs/libc_compat/include/readpassphrase/readpassphrase.h @@ -0,0 +1,44 @@ +/* $OpenBSD: readpassphrase.h,v 1.6 2019/01/25 00:19:25 millert Exp $ */ + +/* + * Copyright (c) 2000, 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +#ifndef _READPASSPHRASE_H_ +#define _READPASSPHRASE_H_ + +#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ +#define RPP_ECHO_ON 0x01 /* Leave echo on. */ +#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ +#define RPP_FORCELOWER 0x04 /* Force input to lower case. */ +#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ +#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ +#define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif +char * readpassphrase(const char *, char *, size_t, int); +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* !_READPASSPHRASE_H_ */ diff --git a/contrib/libs/libc_compat/include/windows/sys/queue.h b/contrib/libs/libc_compat/include/windows/sys/queue.h new file mode 100644 index 00000000000..bc1568be674 --- /dev/null +++ b/contrib/libs/libc_compat/include/windows/sys/queue.h @@ -0,0 +1,631 @@ +/* $OpenBSD: queue.h,v 1.46 2020/12/30 13:33:12 millert Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues and XOR simple queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * An XOR simple queue is used in the same way as a regular simple queue. + * The difference is that the head structure also includes a "cookie" that + * is XOR'd with the queue pointer (first, last or next) to generate the + * real pointer value. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALID ((void *)-1) +#define _Q_INVALIDATE(a) (a) = _Q_INVALID +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST(head); \ + (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ + _Q_INVALIDATE((elm)->field.sle_next); \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods. + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST(head); \ + (var) && ((tvar) = LIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SIMPLEQ_FIRST(head); \ + (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_CONCAT(head1, head2) do { \ + if (!SIMPLEQ_EMPTY((head2))) { \ + *(head1)->sqh_last = (head2)->sqh_first; \ + (head1)->sqh_last = (head2)->sqh_last; \ + SIMPLEQ_INIT((head2)); \ + } \ +} while (0) + +/* + * XOR Simple queue definitions. + */ +#define XSIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqx_first; /* first element */ \ + struct type **sqx_last; /* addr of last next element */ \ + unsigned long sqx_cookie; \ +} + +#define XSIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqx_next; /* next element */ \ +} + +/* + * XOR Simple queue access methods. + */ +#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ + (unsigned long)(ptr))) +#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) +#define XSIMPLEQ_END(head) NULL +#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) +#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) + + +#define XSIMPLEQ_FOREACH(var, head, field) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) != XSIMPLEQ_END(head); \ + (var) = XSIMPLEQ_NEXT(head, var, field)) + +#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ + (var) = (tvar)) + +/* + * XOR Simple queue functions. + */ +#define XSIMPLEQ_INIT(head) do { \ + arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqx_next = (head)->sqx_first) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ + *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + +#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ + (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ + (elm)->field.sqx_next)->field.sqx_next) \ + == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = \ + XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue access methods. + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_END(head) NULL +#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head)) +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = STAILQ_FIRST(head); \ + (var) != STAILQ_END(head); \ + (var) = STAILQ_NEXT(var, field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST(head); \ + (var) && ((tvar) = STAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((elm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((elm), field) = (elm); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ +} while (0) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - offsetof(struct type, field)))) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/contrib/libs/libc_compat/include/windows/sys/uio.h b/contrib/libs/libc_compat/include/windows/sys/uio.h new file mode 100644 index 00000000000..47cc77784ad --- /dev/null +++ b/contrib/libs/libc_compat/include/windows/sys/uio.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define IOV_MAX INT_MAX + +typedef SSIZE_T ssize_t; + +struct iovec { + char* iov_base; + size_t iov_len; +}; + +ssize_t readv(SOCKET sock, struct iovec const* iov, int nvecs); +ssize_t writev(SOCKET sock, struct iovec const* iov, int nvecs); + +#ifdef __cplusplus +} +#endif diff --git a/contrib/libs/libc_compat/memfd_create.c b/contrib/libs/libc_compat/memfd_create.c new file mode 100644 index 00000000000..6805d807d6e --- /dev/null +++ b/contrib/libs/libc_compat/memfd_create.c @@ -0,0 +1,9 @@ +#define _GNU_SOURCE 1 +#include +#include "syscall.h" +#include + +int memfd_create(const char *name, unsigned flags) +{ + return syscall(__NR_memfd_create, name, flags); +} diff --git a/contrib/libs/libc_compat/memrchr.c b/contrib/libs/libc_compat/memrchr.c new file mode 100644 index 00000000000..ff3048759f4 --- /dev/null +++ b/contrib/libs/libc_compat/memrchr.c @@ -0,0 +1,39 @@ +/* $OpenBSD: memrchr.c,v 1.4 2019/01/25 00:19:25 millert Exp $ */ + +/* + * Copyright (c) 2007 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +/* + * Reverse memchr() + * Find the last occurrence of 'c' in the buffer 's' of size 'n'. + */ +void * +memrchr(const void *s, int c, size_t n) +{ + const unsigned char *cp; + + if (n != 0) { + cp = (unsigned char *)s + n; + do { + if (*(--cp) == (unsigned char)c) + return((void *)cp); + } while (--n != 0); + } + return(NULL); +} + diff --git a/contrib/libs/libc_compat/random/getentropy.c b/contrib/libs/libc_compat/random/getentropy.c new file mode 100644 index 00000000000..50e9c2fae25 --- /dev/null +++ b/contrib/libs/libc_compat/random/getentropy.c @@ -0,0 +1,37 @@ +#define _BSD_SOURCE +#include +#include +#include +#include + +int getentropy(void *buffer, size_t len) +{ + int cs, ret = 0; + char *pos = buffer; + + if (len > 256) { + errno = EIO; + return -1; + } + +#if defined(__linux__) && !defined(__ANDROID__) + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); +#endif + + while (len) { + ret = getrandom(pos, len, 0); + if (ret < 0) { + if (errno == EINTR) continue; + else break; + } + pos += ret; + len -= ret; + ret = 0; + } + +#if defined(__linux__) && !defined(__ANDROID__) + pthread_setcancelstate(cs, 0); +#endif + + return ret; +} diff --git a/contrib/libs/libc_compat/random/getrandom.c b/contrib/libs/libc_compat/random/getrandom.c new file mode 100644 index 00000000000..ba2d393c6b6 --- /dev/null +++ b/contrib/libs/libc_compat/random/getrandom.c @@ -0,0 +1,18 @@ +#include +#include "syscall.h" + +#if defined(__has_feature) + #if __has_feature(memory_sanitizer) + #include + #endif +#endif + +ssize_t getrandom(void *buf, size_t buflen, unsigned flags) +{ +#if defined(__has_feature) + #if __has_feature(memory_sanitizer) + __msan_unpoison(buf, buflen); + #endif +#endif + return syscall(SYS_getrandom, buf, buflen, flags); +} diff --git a/contrib/libs/libc_compat/random/sys/random.h b/contrib/libs/libc_compat/random/sys/random.h new file mode 100644 index 00000000000..c33843ea23b --- /dev/null +++ b/contrib/libs/libc_compat/random/sys/random.h @@ -0,0 +1,33 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if !defined(SYS_getrandom) +#if defined(__x86_64__) + #define SYS_getrandom 318 +#elif defined(__i386__) + #define SYS_getrandom 355 +#elif defined(__aarch64__) + #define SYS_getrandom 278 +#elif defined(__arm__) + #define SYS_getrandom 384 +#elif defined(__powerpc__) + #define SYS_getrandom 359 +#else +#error Unsupported platform +#endif +#endif + +#define GRND_NONBLOCK 0x0001 +#define GRND_RANDOM 0x0002 +#define GRND_INSECURE 0x0004 + +ssize_t getrandom(void* buf, size_t buflen, unsigned int flags); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/contrib/libs/libc_compat/readpassphrase.c b/contrib/libs/libc_compat/readpassphrase.c new file mode 100644 index 00000000000..df677e9ca5e --- /dev/null +++ b/contrib/libs/libc_compat/readpassphrase.c @@ -0,0 +1,192 @@ +/* $OpenBSD: readpassphrase.c,v 1.27 2019/01/25 00:19:25 millert Exp $ */ + +/* + * Copyright (c) 2000-2002, 2007, 2010 + * Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef TCSASOFT +/* If we don't have TCSASOFT define it so that ORing it it below is a no-op. */ +# define TCSASOFT 0 +#endif + +/* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ +#if !defined(_POSIX_VDISABLE) && defined(VDISABLE) +# define _POSIX_VDISABLE VDISABLE +#endif + +static volatile sig_atomic_t signo[_NSIG]; + +static void handler(int); + +char * +readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) +{ + ssize_t nr; + int input, output, save_errno, i, need_restart; + char ch, *p, *end; + struct termios term, oterm; + struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; + struct sigaction savetstp, savettin, savettou, savepipe; + + /* I suppose we could alloc on demand in this case (XXX). */ + if (bufsiz == 0) { + errno = EINVAL; + return(NULL); + } + +restart: + for (i = 0; i < _NSIG; i++) + signo[i] = 0; + nr = -1; + save_errno = 0; + need_restart = 0; + /* + * Read and write to /dev/tty if available. If not, read from + * stdin and write to stderr unless a tty is required. + */ + if ((flags & RPP_STDIN) || + (input = output = open(_PATH_TTY, O_RDWR)) == -1) { + if (flags & RPP_REQUIRE_TTY) { + errno = ENOTTY; + return(NULL); + } + input = STDIN_FILENO; + output = STDERR_FILENO; + } + + /* + * Turn off echo if possible. + * If we are using a tty but are not the foreground pgrp this will + * generate SIGTTOU, so do it *before* installing the signal handlers. + */ + if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { + memcpy(&term, &oterm, sizeof(term)); + if (!(flags & RPP_ECHO_ON)) + term.c_lflag &= ~(ECHO | ECHONL); + (void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term); + } else { + memset(&term, 0, sizeof(term)); + term.c_lflag |= ECHO; + memset(&oterm, 0, sizeof(oterm)); + oterm.c_lflag |= ECHO; + } + + /* + * Catch signals that would otherwise cause the user to end + * up with echo turned off in the shell. Don't worry about + * things like SIGXCPU and SIGVTALRM for now. + */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; /* don't restart system calls */ + sa.sa_handler = handler; + (void)sigaction(SIGALRM, &sa, &savealrm); + (void)sigaction(SIGHUP, &sa, &savehup); + (void)sigaction(SIGINT, &sa, &saveint); + (void)sigaction(SIGPIPE, &sa, &savepipe); + (void)sigaction(SIGQUIT, &sa, &savequit); + (void)sigaction(SIGTERM, &sa, &saveterm); + (void)sigaction(SIGTSTP, &sa, &savetstp); + (void)sigaction(SIGTTIN, &sa, &savettin); + (void)sigaction(SIGTTOU, &sa, &savettou); + + if (!(flags & RPP_STDIN)) + (void)write(output, prompt, strlen(prompt)); + end = buf + bufsiz - 1; + p = buf; + while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { + if (p < end) { + if ((flags & RPP_SEVENBIT)) + ch &= 0x7f; + if (isalpha((unsigned char)ch)) { + if ((flags & RPP_FORCELOWER)) + ch = (char)tolower((unsigned char)ch); + if ((flags & RPP_FORCEUPPER)) + ch = (char)toupper((unsigned char)ch); + } + *p++ = ch; + } + } + *p = '\0'; + save_errno = errno; + if (!(term.c_lflag & ECHO)) + (void)write(output, "\n", 1); + + /* Restore old terminal settings and signals. */ + if (memcmp(&term, &oterm, sizeof(term)) != 0) { + const int sigttou = signo[SIGTTOU]; + + /* Ignore SIGTTOU generated when we are not the fg pgrp. */ + while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 && + errno == EINTR && !signo[SIGTTOU]) + continue; + signo[SIGTTOU] = sigttou; + } + (void)sigaction(SIGALRM, &savealrm, NULL); + (void)sigaction(SIGHUP, &savehup, NULL); + (void)sigaction(SIGINT, &saveint, NULL); + (void)sigaction(SIGQUIT, &savequit, NULL); + (void)sigaction(SIGPIPE, &savepipe, NULL); + (void)sigaction(SIGTERM, &saveterm, NULL); + (void)sigaction(SIGTSTP, &savetstp, NULL); + (void)sigaction(SIGTTIN, &savettin, NULL); + (void)sigaction(SIGTTOU, &savettou, NULL); + if (input != STDIN_FILENO) + (void)close(input); + + /* + * If we were interrupted by a signal, resend it to ourselves + * now that we have restored the signal handlers. + */ + for (i = 0; i < _NSIG; i++) { + if (signo[i]) { + kill(getpid(), i); + switch (i) { + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + need_restart = 1; + } + } + } + if (need_restart) + goto restart; + + if (save_errno) + errno = save_errno; + return(nr == -1 ? NULL : buf); +} + + +static void handler(int s) +{ + + signo[s] = 1; +} diff --git a/contrib/libs/libc_compat/reallocarray/reallocarray.c b/contrib/libs/libc_compat/reallocarray/reallocarray.c new file mode 100644 index 00000000000..21b0914fe48 --- /dev/null +++ b/contrib/libs/libc_compat/reallocarray/reallocarray.c @@ -0,0 +1,39 @@ +/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} + diff --git a/contrib/libs/libc_compat/reallocarray/stdlib.h b/contrib/libs/libc_compat/reallocarray/stdlib.h new file mode 100644 index 00000000000..13496a79d00 --- /dev/null +++ b/contrib/libs/libc_compat/reallocarray/stdlib.h @@ -0,0 +1,24 @@ +// The lack of #pragma once is intentional: +// its presence breaks compilation of contrib/tools/unbound somehow. + +#if defined(__GNUC__) || defined(__clang__) + #include_next +#else + #ifdef Y_UCRT_INCLUDE_NEXT + #include Y_UCRT_INCLUDE_NEXT(stdlib.h) + #else + #define Y_UCRT_INCLUDE_NEXT(x) + #include Y_UCRT_INCLUDE_NEXT(stdlib.h) + #undef Y_UCRT_INCLUDE_NEXT + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +void* reallocarray(void*, size_t, size_t); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/contrib/libs/libc_compat/src/windows/sys/uio.c b/contrib/libs/libc_compat/src/windows/sys/uio.c new file mode 100644 index 00000000000..50c9542dc14 --- /dev/null +++ b/contrib/libs/libc_compat/src/windows/sys/uio.c @@ -0,0 +1,36 @@ +#include + +#include +#include +#include + +ssize_t readv(SOCKET sock, struct iovec const* iov, int iovcnt) { + WSABUF* wsabuf = (WSABUF*)alloca(iovcnt * sizeof(WSABUF)); + for (int i = 0; i < iovcnt; ++i) { + wsabuf[i].buf = iov[i].iov_base; + wsabuf[i].len = (u_long)iov[i].iov_len; + } + DWORD numberOfBytesRecv; + DWORD flags = 0; + int res = WSARecv(sock, wsabuf, iovcnt, &numberOfBytesRecv, &flags, NULL, NULL); + if (res == SOCKET_ERROR) { + errno = EIO; + return -1; + } + return numberOfBytesRecv; +} + +ssize_t writev(SOCKET sock, struct iovec const* iov, int iovcnt) { + WSABUF* wsabuf = (WSABUF*)alloca(iovcnt * sizeof(WSABUF)); + for (int i = 0; i < iovcnt; ++i) { + wsabuf[i].buf = iov[i].iov_base; + wsabuf[i].len = (u_long)iov[i].iov_len; + } + DWORD numberOfBytesSent; + int res = WSASend(sock, wsabuf, iovcnt, &numberOfBytesSent, 0, NULL, NULL); + if (res == SOCKET_ERROR) { + errno = EIO; + return -1; + } + return numberOfBytesSent; +} diff --git a/contrib/libs/libc_compat/stpcpy.c b/contrib/libs/libc_compat/stpcpy.c new file mode 100644 index 00000000000..5a86541f080 --- /dev/null +++ b/contrib/libs/libc_compat/stpcpy.c @@ -0,0 +1,44 @@ +/* $OpenBSD: stpcpy.c,v 1.3 2017/11/28 06:55:49 tb Exp $ */ + +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#if defined(APIWARN) +__warn_references(stpcpy, + "stpcpy() is dangerous; do not use it"); +#endif + +char * +stpcpy(char *to, const char *from) +{ + for (; (*to = *from) != '\0'; ++from, ++to); + return(to); +} diff --git a/contrib/libs/libc_compat/strcasestr.c b/contrib/libs/libc_compat/strcasestr.c new file mode 100644 index 00000000000..ee299b1c01f --- /dev/null +++ b/contrib/libs/libc_compat/strcasestr.c @@ -0,0 +1,61 @@ +/* $OpenBSD: strcasestr.c,v 1.4 2015/08/31 02:53:57 guenther Exp $ */ +/* $NetBSD: strcasestr.c,v 1.2 2005/02/09 21:35:47 kleink Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include "string.h" + +/* + * Find the first occurrence of find in s, ignore case. + */ +char * +strcasestr(const char *s, const char *find) +{ + char c, sc; + size_t len; + + if ((c = *find++) != 0) { + c = (char)tolower((unsigned char)c); + len = strlen(find); + do { + do { + if ((sc = *s++) == 0) + return (NULL); + } while ((char)tolower((unsigned char)sc) != c); + } while (strncasecmp(s, find, len) != 0); + s--; + } + return ((char *)s); +} + diff --git a/contrib/libs/libc_compat/string.c b/contrib/libs/libc_compat/string.c new file mode 100644 index 00000000000..291f1103617 --- /dev/null +++ b/contrib/libs/libc_compat/string.c @@ -0,0 +1,16 @@ +#include +#include + +char* strupr(char* s) { + char* d; + for (d = s; *d; ++d) + *d = (char)toupper((int)*d); + return s; +} + +char* strlwr(char* s) { + char* d; + for (d = s; *d; ++d) + *d = (char)tolower((int)*d); + return s; +} diff --git a/contrib/libs/libc_compat/string.h b/contrib/libs/libc_compat/string.h new file mode 100644 index 00000000000..ad74d9a6300 --- /dev/null +++ b/contrib/libs/libc_compat/string.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__FreeBSD__) && !defined(__APPLE__) +size_t strlcpy(char* dst, const char* src, size_t len); +size_t strlcat(char* dst, const char* src, size_t len); +#endif + +#if (!defined(__linux__) && !defined(__FreeBSD__) && !defined(__APPLE__)) || (defined(__ANDROID__) && __ANDROID_API__ < 21) +char* stpcpy(char* dst, const char* src); +#endif + +#if !defined(_MSC_VER) + +#define stricmp strcasecmp +#define strnicmp strncasecmp + +char* strlwr(char*); +char* strupr(char*); + +#else // _MSC_VER + +#define strcasecmp stricmp +#define strncasecmp strnicmp + +char* strcasestr(const char* s1, const char* s2); +char* strsep(char** stringp, const char* delim); + +#endif // _MSC_VER + +#if defined(_MSC_VER) || defined(__APPLE__) +void* memrchr(const void* s, int c, size_t n); +#endif + +#ifdef __cplusplus +} //extern "C" +#endif diff --git a/contrib/libs/libc_compat/strlcat.c b/contrib/libs/libc_compat/strlcat.c new file mode 100644 index 00000000000..d2198a2bdcd --- /dev/null +++ b/contrib/libs/libc_compat/strlcat.c @@ -0,0 +1,56 @@ +/* $OpenBSD: strlcat.c,v 1.19 2019/01/25 00:19:25 millert Exp $ */ + +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t dsize) +{ + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return(dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return(dlen + (src - osrc)); /* count does not include NUL */ +} + diff --git a/contrib/libs/libc_compat/strlcpy.c b/contrib/libs/libc_compat/strlcpy.c new file mode 100644 index 00000000000..1797b86bb6e --- /dev/null +++ b/contrib/libs/libc_compat/strlcpy.c @@ -0,0 +1,51 @@ +/* $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $ */ + +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +} + diff --git a/contrib/libs/libc_compat/strsep.c b/contrib/libs/libc_compat/strsep.c new file mode 100644 index 00000000000..ed605bce977 --- /dev/null +++ b/contrib/libs/libc_compat/strsep.c @@ -0,0 +1,71 @@ +/* $OpenBSD: strsep.c,v 1.8 2015/08/31 02:53:57 guenther Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char * +strsep(char **stringp, const char *delim) +{ + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + diff --git a/contrib/libs/libc_compat/ubuntu_14/README.md b/contrib/libs/libc_compat/ubuntu_14/README.md new file mode 100644 index 00000000000..f7b17a40cc0 --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/README.md @@ -0,0 +1,91 @@ +## Общие соображения + +В библиотеку добавлены реализации символов, появившихся в `libc.so.6` Ubuntu +14.04 с момента Ubuntu 12.04. + +Если какой-либо объектный файл ссылается на один из таких символов, то при +компоновке этот символ будет взят из нашей библиотеки. В противном случае, +компоновщик сослался бы на такой символ из `libc.so.6`, добавив к исполняемому +файлу зависимость от новой версии динамической библиотеки (`GLIBC_2.16`, +`GLIBC_2.17` или `GLIBC_2.18`). Такой исполняемый файл не может быть запущен на +Ubuntu 12.04, даже если ни один из новых символов не используется в runtime. + +На Ubuntu 14.04 или более новых, в случае если процесс загружает `libc.so.6`, мы +будем в runtime иметь две реализации символов, нашу и libc. Но все добавленные +функции не имеют какого-либо состояния и никакими деталями внутренней реализации +не связаны с другими. Нет разницы, какую из реализаций использовать, и даже +попеременное использование различных реализаций в одном и том же контексте не +должно приводить к некорректной работе. + +В какой-то момент этот слой совместимости будет отключен: https://st.yandex-team.ru/DEVTOOLS-7436 + +### Разделяемая реализация + +Была идея оформить новые реализации так, чтобы в случае их наличия и в +загруженной libc динамический компоновщик выбирал для всех ссылок одну +реализацию. + +По всей видимости, для этого требуется собирать исполняемый файл как PIE. Только +в этом случае для наших реализаций будут сгенерированы дополнительные PLT +прослойки и вызовы наших реализаций будут проходить через них. + +Но в этом случае вообще все вызовы будут происходить таким образом и это +повлияет на производительность. Ухудшения производительности, наверное, можно +избежать, явно указав, какие символы в исполняемом файле должны быть публичными, +но сейчас нет способа сделать это в одном месте, учитывая, что в некоторых +случаях этот список должен дополняться. + +## `getauxval` и `secure_getenv` + +Функция `getauxval` требует загрузки и хранения «Auxiliary Vector» (см. +[здесь](https://refspecs.linuxfoundation.org/LSB_1.3.0/IA64/spec/auxiliaryvector.html)). + +Эти данные доступны в момент запуска процесса. libc из новых Ubuntu сохраняет +эти данные и предоставляет реализацию `getauxval`. В этом случае наша реализация +перенаправляет вызовы `getauxval` в libc, получив при старте исполняемого файла +соответствующий указатель. + +Если реализация libc недоступна (на старых Ubuntu или если libc не загружена), +то эти данные можно получить, прочитав `/proc/self/auxv`. Это также делается +один раз при старте. + +В обоих случаях, статически инициализируется синглтон `NUbuntuCompat::TGlibc`, +который производит эти действия в конструкторе. + +`secure_getenv` использует одно из значений `getauxval`, поэтому к нему всё это +также относится. + +Каждый метод новой libc реализован в отдельном объектом файле. `TGlibc` также +находится в отдельном файле, и ссылки на неё стоят только в местах использования. +Если при компоновке не понадобились ни `getauxval`, ни `secure_getenv`, то +объектный файл с `TGlibc` тоже не будет выбран компоновщиком, и в этом случае +никакой лишней статической инициализации выполняться не будет. + +## Патч libc.so + +Чтобы иметь возможность использовать `getauxval` (точнее его реализацию +`__getauxval`) из новой libc, если таковая уже используется процессом, +библиотека совместимости объявляет этот символ у себя как внешний и слабый. При +загрузке динамический компоновщик устанавливает значение этого символа из libc +или `nullptr`, если его там нет. Наша реализация `getauxval` проверяет +доступность реализации libc, просто сравнивая указатель с `nullptr`. + +В Аркадии также есть код, который подобным образом работает с символом +`__cxa_thread_atexit_impl`. + +Однако, если компоновать такую программу с использованием новой libc, то к таким +символам и самой программе будет приписано требование соответствующей (новой) +версии libc. Чтобы этого не произошло, при сборке с этой библиотекой +совместимости используется патченный вариант `libc.so.6`, где у таких символов +удалена версия. + +Также, файлы, проверяющие, что доступна реализация из libc, должны быть собраны +как PIC. В противном случае вместо значения, заполненного динамическим +компоновщиком, компилятор ещё на стадии компиляции использует `nullptr` и +проверка никогда не срабатывает. + +## Упоминания + +Идея о возможности добавить слой совместимости была взята из ClickHouse. +* [https://clickhouse.tech/](https://clickhouse.tech/) +* [https://wiki.yandex-team.ru/clickhouse/](https://wiki.yandex-team.ru/clickhouse/) diff --git a/contrib/libs/libc_compat/ubuntu_14/aligned_alloc.c b/contrib/libs/libc_compat/ubuntu_14/aligned_alloc.c new file mode 100644 index 00000000000..c4a1378624f --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/aligned_alloc.c @@ -0,0 +1,6 @@ +#include +#include + +__attribute__((weak)) void* aligned_alloc(size_t alignment, size_t size) { + return memalign(alignment, size); +} diff --git a/contrib/libs/libc_compat/ubuntu_14/c16rtomb.c b/contrib/libs/libc_compat/ubuntu_14/c16rtomb.c new file mode 100644 index 00000000000..39ca3758fa1 --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/c16rtomb.c @@ -0,0 +1,35 @@ +#include +#include +#include + +size_t c16rtomb(char *restrict s, char16_t c16, mbstate_t *restrict ps) +{ + static unsigned internal_state; + if (!ps) ps = (void *)&internal_state; + unsigned *x = (unsigned *)ps; + wchar_t wc; + + if (!s) { + if (*x) goto ilseq; + return 1; + } + + if (!*x && c16 - 0xd800u < 0x400) { + *x = c16 - 0xd7c0 << 10; + return 0; + } + + if (*x) { + if (c16 - 0xdc00u >= 0x400) goto ilseq; + else wc = *x + c16 - 0xdc00; + *x = 0; + } else { + wc = c16; + } + return wcrtomb(s, wc, 0); + +ilseq: + *x = 0; + errno = EILSEQ; + return -1; +} diff --git a/contrib/libs/libc_compat/ubuntu_14/c32rtomb.c b/contrib/libs/libc_compat/ubuntu_14/c32rtomb.c new file mode 100644 index 00000000000..67851328e88 --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/c32rtomb.c @@ -0,0 +1,7 @@ +#include +#include + +size_t c32rtomb(char *restrict s, char32_t c32, mbstate_t *restrict ps) +{ + return wcrtomb(s, c32, ps); +} diff --git a/contrib/libs/libc_compat/ubuntu_14/features.h b/contrib/libs/libc_compat/ubuntu_14/features.h new file mode 100644 index 00000000000..9fbab45ab5b --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/features.h @@ -0,0 +1,5 @@ +#pragma once + +#define weak __attribute__((__weak__)) +#define weak_alias(old, new) \ + extern __typeof(old) new __attribute__((__weak__, __alias__(#old))) diff --git a/contrib/libs/libc_compat/ubuntu_14/getauxval.cpp b/contrib/libs/libc_compat/ubuntu_14/getauxval.cpp new file mode 100644 index 00000000000..9f20dd01953 --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/getauxval.cpp @@ -0,0 +1,10 @@ +#include + +#include "glibc.h" +#include "features.h" + +extern "C" { + unsigned long getauxval(unsigned long item) noexcept { + return NUbuntuCompat::GetGlibc().GetAuxVal(item); + } +} diff --git a/contrib/libs/libc_compat/ubuntu_14/glibc.cpp b/contrib/libs/libc_compat/ubuntu_14/glibc.cpp new file mode 100644 index 00000000000..1cc444bce1f --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/glibc.cpp @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "glibc.h" +#include "features.h" + +namespace { + void ReadAuxVector(Elf64_auxv_t** begin, Elf64_auxv_t** end) noexcept { + int fd = open("/proc/self/auxv", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + return; + } + + constexpr size_t item_size = sizeof(Elf64_auxv_t); + constexpr size_t block_size = item_size * 32; + + size_t bytes_read = 0; + size_t size = 0; + + struct TBuffer { + ~TBuffer() { + free(Pointer); + } + char* Pointer = nullptr; + } buffer; + + while (true) { + size_t bytes_left = size - bytes_read; + + if (!bytes_left) { + size += block_size; + char* new_buffer = (char*)realloc(buffer.Pointer, size); + if (!new_buffer) { + return; + } + buffer.Pointer = new_buffer; + continue; + } + + ssize_t r = read(fd, buffer.Pointer + bytes_read, bytes_left); + if (!r) { + break; + } else if (r < 0) { + if (errno == EINTR) { + continue; + } else { + return; + } + } + + bytes_read += r; + } + + size_t item_count = bytes_read / item_size; + *begin = (Elf64_auxv_t*)buffer.Pointer; + *end = (Elf64_auxv_t*)(buffer.Pointer + item_count * item_size); + buffer.Pointer = nullptr; + } +} + +extern "C" { + weak unsigned long __getauxval(unsigned long item); +} + +namespace NUbuntuCompat { + + TGlibc::TGlibc() noexcept + : AuxVectorBegin(nullptr) + , AuxVectorEnd(nullptr) + { + if (!__getauxval) { + ReadAuxVector((Elf64_auxv_t**)&AuxVectorBegin, (Elf64_auxv_t**)&AuxVectorEnd); + } + + Secure = (bool)GetAuxVal(AT_SECURE); + } + + TGlibc::~TGlibc() noexcept { + free(AuxVectorBegin); + } + + unsigned long TGlibc::GetAuxVal(unsigned long item) noexcept { + if (__getauxval) { + return __getauxval(item); + } + + for (Elf64_auxv_t* p = (Elf64_auxv_t*)AuxVectorBegin; p < (Elf64_auxv_t*)AuxVectorEnd; ++p) { + if (p->a_type == item) { + return p->a_un.a_val; + } + } + + errno = ENOENT; + return 0; + } + + bool TGlibc::IsSecure() noexcept { + return Secure; + } + + static TGlibc __attribute__((__init_priority__(101))) GlibcInstance; + + TGlibc& GetGlibc() noexcept { + return GlibcInstance; + } +} diff --git a/contrib/libs/libc_compat/ubuntu_14/glibc.h b/contrib/libs/libc_compat/ubuntu_14/glibc.h new file mode 100644 index 00000000000..fdabcb01581 --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/glibc.h @@ -0,0 +1,20 @@ +#pragma once + +typedef unsigned long (*TGetAuxVal)(unsigned long); + +namespace NUbuntuCompat { + class TGlibc { + public: + TGlibc() noexcept; + ~TGlibc() noexcept; + unsigned long GetAuxVal(unsigned long item) noexcept; + bool IsSecure() noexcept; + + private: + void* AuxVectorBegin; + void* AuxVectorEnd; + bool Secure; + }; + + TGlibc& GetGlibc() noexcept; +} diff --git a/contrib/libs/libc_compat/ubuntu_14/mbrtoc16.c b/contrib/libs/libc_compat/ubuntu_14/mbrtoc16.c new file mode 100644 index 00000000000..765ff9037cd --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/mbrtoc16.c @@ -0,0 +1,30 @@ +#include +#include + +size_t mbrtoc16(char16_t *restrict pc16, const char *restrict s, size_t n, mbstate_t *restrict ps) +{ + static unsigned internal_state; + if (!ps) ps = (void *)&internal_state; + unsigned *pending = (unsigned *)ps; + + if (!s) return mbrtoc16(0, "", 1, ps); + + /* mbrtowc states for partial UTF-8 characters have the high bit set; + * we use nonzero states without high bit for pending surrogates. */ + if ((int)*pending > 0) { + if (pc16) *pc16 = *pending; + *pending = 0; + return -3; + } + + wchar_t wc; + size_t ret = mbrtowc(&wc, s, n, ps); + if (ret <= 4) { + if (wc >= 0x10000) { + *pending = (wc & 0x3ff) + 0xdc00; + wc = 0xd7c0 + (wc >> 10); + } + if (pc16) *pc16 = wc; + } + return ret; +} diff --git a/contrib/libs/libc_compat/ubuntu_14/mbrtoc32.c b/contrib/libs/libc_compat/ubuntu_14/mbrtoc32.c new file mode 100644 index 00000000000..9b6b2367398 --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/mbrtoc32.c @@ -0,0 +1,13 @@ +#include +#include + +size_t mbrtoc32(char32_t *restrict pc32, const char *restrict s, size_t n, mbstate_t *restrict ps) +{ + static unsigned internal_state; + if (!ps) ps = (void *)&internal_state; + if (!s) return mbrtoc32(0, "", 1, ps); + wchar_t wc; + size_t ret = mbrtowc(&wc, s, n, ps); + if (ret <= 4 && pc32) *pc32 = wc; + return ret; +} diff --git a/contrib/libs/libc_compat/ubuntu_14/secure_getenv.cpp b/contrib/libs/libc_compat/ubuntu_14/secure_getenv.cpp new file mode 100644 index 00000000000..14e3e90906a --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/secure_getenv.cpp @@ -0,0 +1,12 @@ +#include + +#include "glibc.h" + +extern "C" { + char *secure_getenv(const char *name) noexcept { + if (NUbuntuCompat::GetGlibc().IsSecure()) { + return nullptr; + } + return getenv(name); + } +} diff --git a/contrib/libs/libc_compat/ubuntu_14/timespec_get.c b/contrib/libs/libc_compat/ubuntu_14/timespec_get.c new file mode 100644 index 00000000000..742b62ec847 --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/timespec_get.c @@ -0,0 +1,10 @@ +#include + +/* There is no other implemented value than TIME_UTC; all other values + * are considered erroneous. */ +int timespec_get(struct timespec * ts, int base) +{ + if (base != TIME_UTC) return 0; + int ret = clock_gettime(CLOCK_REALTIME, ts); + return ret < 0 ? 0 : base; +} diff --git a/contrib/libs/libc_compat/ubuntu_14/ya.make b/contrib/libs/libc_compat/ubuntu_14/ya.make new file mode 100644 index 00000000000..3690c8613a3 --- /dev/null +++ b/contrib/libs/libc_compat/ubuntu_14/ya.make @@ -0,0 +1,36 @@ +LIBRARY() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(BSD-3-Clause) + +NO_PLATFORM() + +NO_RUNTIME() + +NO_UTIL() + +DISABLE(NEED_PLATFORM_PEERDIRS) +DISABLE(OPENSOURCE_EXPORT) + +IF (OS_SDK == "ubuntu-14") + PEERDIR( + build/platform/linux_sdk + ) + SRCS( + aligned_alloc.c + c16rtomb.c + c32rtomb.c + getauxval.cpp + mbrtoc16.c + mbrtoc32.c + secure_getenv.cpp + timespec_get.c + ) + SRC_C_PIC( + glibc.cpp + -fno-lto + ) +ENDIF() + +END() diff --git a/contrib/libs/libc_compat/ya.make b/contrib/libs/libc_compat/ya.make new file mode 100644 index 00000000000..ad8374b5a60 --- /dev/null +++ b/contrib/libs/libc_compat/ya.make @@ -0,0 +1,161 @@ +LIBRARY() + +LICENSE( + BSD-1-Clause AND + BSD-2-Clause AND + BSD-3-Clause AND + ISC +) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +NO_RUNTIME() + +IF (NOT OS_WINDOWS) + SRCS( + string.c + ) +ENDIF() + +DISABLE(PROVIDE_GETRANDOM_GETENTROPY) +DISABLE(PROVIDE_REALLOCARRAY) + +# Android libc function appearance is documented here: +# https://android.googlesource.com/platform/bionic/+/master/docs/status.md +# +# NB: nested IF's are needed due to the lack of lazy evaluation of logical statements: DEVTOOLS-7837 +IF (OS_ANDROID) + SRCS( + strlcat.c + strlcpy.c + ) + + IF (ANDROID_API < 28) + SRCS( + glob.c + ) + ENABLE(PROVIDE_GETRANDOM_GETENTROPY) + ENABLE(PROVIDE_REALLOCARRAY) + ENDIF() + IF (ANDROID_API < 24) + SRCS( + ifaddrs.c + ) + ADDINCL( + GLOBAL contrib/libs/libc_compat/include/ifaddrs + ) + ENDIF() + IF (ANDROID_API < 21) + SRCS( + stpcpy.c + ) + ENDIF() +ENDIF() + +IF (OS_WINDOWS OR OS_DARWIN OR OS_IOS) + SRCS( + memrchr.c + ) +ENDIF() + +IF (OS_DARWIN) + SRCS( + explicit_bzero.c + ) + ENABLE(PROVIDE_REALLOCARRAY) +ENDIF() + +IF (OS_WINDOWS) + ADDINCL( + GLOBAL contrib/libs/libc_compat/include/windows + ) + SRCS( + explicit_bzero.c + stpcpy.c + strlcat.c + strlcpy.c + strcasestr.c + strsep.c + src/windows/sys/uio.c + ) + ENABLE(PROVIDE_REALLOCARRAY) +ENDIF() + +IF (OS_LINUX) + ADDINCL( + GLOBAL contrib/libs/libc_compat/include/readpassphrase + ) + SRCS( + readpassphrase.c + ) +ENDIF() + +IF (OS_LINUX AND NOT MUSL) + IF (OS_SDK == "ubuntu-12") + ADDINCL( + # uchar.h was introduced in glibc=2.16 + GLOBAL contrib/libs/libc_compat/include/uchar + ) + ENDIF() + IF (OS_SDK == "ubuntu-12" OR OS_SDK == "ubuntu-14" OR OS_SDK == "ubuntu-16") + # getrandom and getentropy were added in glibc=2.25 + ENABLE(PROVIDE_GETRANDOM_GETENTROPY) + + SRCS( + # explicit_bzero was added in glibc=2.25 + explicit_bzero.c + # memfd_create was added in glibc=2.27 + memfd_create.c + ) + ENDIF() + IF (OS_SDK != "ubuntu-20" AND OS_SDK != "ubuntu-22") + # reallocarray was added in glibc=2.29 + ENABLE(PROVIDE_REALLOCARRAY) + ENDIF() + SRCS( + # glibc does not offer strlcat / strlcpy yet + strlcat.c + strlcpy.c + ) + IF (SANITIZER_TYPE == "memory") + # llvm sanitized runtime is missing an interceptor for a buggy (getservbyname{_r}). + # See: https://github.com/google/sanitizers/issues/1138 + ENABLE(PROVIDE_GETSERVBYNAME) + ENDIF() +ENDIF() + +IF (PROVIDE_REALLOCARRAY) + SRCS( + reallocarray/reallocarray.c + ) + ADDINCL( + GLOBAL contrib/libs/libc_compat/reallocarray + ) +ENDIF() + +IF (PROVIDE_GETRANDOM_GETENTROPY) + PEERDIR( + library/cpp/sanitizer/include + ) + SRCS( + random/getrandom.c + random/getentropy.c + ) + ADDINCL( + GLOBAL contrib/libs/libc_compat/random + ) +ENDIF() + +IF (PROVIDE_GETSERVBYNAME) + SRCS( + getservbyname/getservbyname.c + getservbyname/getservbyname_r.c + getservbyname/lookup_serv.c + ) +ENDIF() + +END() diff --git a/include/ydb-cpp-sdk/library/string_utils/misc/misc.h b/include/ydb-cpp-sdk/library/string_utils/misc/misc.h index f94f35382eb..21fa4b3fa59 100644 --- a/include/ydb-cpp-sdk/library/string_utils/misc/misc.h +++ b/include/ydb-cpp-sdk/library/string_utils/misc/misc.h @@ -2,8 +2,8 @@ #include -#include -#include +#include +#include #include diff --git a/include/ydb-cpp-sdk/stlfwd.h b/include/ydb-cpp-sdk/stlfwd.h new file mode 100644 index 00000000000..77b97233f95 --- /dev/null +++ b/include/ydb-cpp-sdk/stlfwd.h @@ -0,0 +1,15 @@ +#pragma once + +// STL "forwarding" for the poor + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 11245e6d782..d2ae71a6cb5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,5 +2,5 @@ add_subdirectory(api) add_subdirectory(client) add_subdirectory(json_value) add_subdirectory(library) -add_subdirectory(util) +#add_subdirectory(util) add_subdirectory(yson_value) \ No newline at end of file diff --git a/src/library/cppparser/parser.cpp b/src/library/cppparser/parser.cpp index 53cdd2d2e37..b4e55cb55b8 100644 --- a/src/library/cppparser/parser.cpp +++ b/src/library/cppparser/parser.cpp @@ -1,7 +1,7 @@ #include "parser.h" -#include -#include +#include +#include #include diff --git a/src/library/cppparser/parser.h b/src/library/cppparser/parser.h index bd96be87025..9c90dc1b5fe 100644 --- a/src/library/cppparser/parser.h +++ b/src/library/cppparser/parser.h @@ -1,8 +1,8 @@ #pragma once -#include +#include #include -#include +#include class TCppSaxParser: public IOutputStream { public: diff --git a/src/library/cpuid_check/cpu_id_check.cpp b/src/library/cpuid_check/cpu_id_check.cpp index 29ebef2ce4d..f19a5735671 100644 --- a/src/library/cpuid_check/cpu_id_check.cpp +++ b/src/library/cpuid_check/cpu_id_check.cpp @@ -1,7 +1,7 @@ -#include -#include -#include -#include +#include +#include +#include +#include #define Y_CPU_ID_ENUMERATE_STARTUP_CHECKS(F) \ F(SSE42) \ diff --git a/src/library/string_utils/base64/base64.cpp b/src/library/string_utils/base64/base64.cpp index e36f249a33e..1b18220b496 100644 --- a/src/library/string_utils/base64/base64.cpp +++ b/src/library/string_utils/base64/base64.cpp @@ -1,9 +1,9 @@ #include "base64.h" #include -#include -#include -#include +#include +#include +#include #include diff --git a/src/library/string_utils/quote/quote.cpp b/src/library/string_utils/quote/quote.cpp index 977e807b56c..cbb49f902ff 100644 --- a/src/library/string_utils/quote/quote.cpp +++ b/src/library/string_utils/quote/quote.cpp @@ -1,8 +1,8 @@ #include "quote.h" -#include -#include -#include +#include +#include +#include /* note: (x & 0xdf) makes x upper case */ #define GETXC \ diff --git a/src/library/string_utils/relaxed_escaper/relaxed_escaper.h b/src/library/string_utils/relaxed_escaper/relaxed_escaper.h index 5d11a4b7c68..a8551de5026 100644 --- a/src/library/string_utils/relaxed_escaper/relaxed_escaper.h +++ b/src/library/string_utils/relaxed_escaper/relaxed_escaper.h @@ -1,8 +1,8 @@ #pragma once -#include -#include -#include +#include +#include +#include #include namespace NEscJ { diff --git a/src/library/string_utils/scan/CMakeLists.txt b/src/library/string_utils/scan/CMakeLists.txt index 0d93375f97d..5ab32aae0dc 100644 --- a/src/library/string_utils/scan/CMakeLists.txt +++ b/src/library/string_utils/scan/CMakeLists.txt @@ -1,7 +1,7 @@ add_library(string_utils-scan) target_link_libraries(string_utils-scan PUBLIC - yutil + string_utils-helpers ) target_sources(string_utils-scan PRIVATE diff --git a/src/library/string_utils/stream/stream.h b/src/library/string_utils/stream/stream.h index ec45d9e0cdf..c4f9a88297b 100644 --- a/src/library/string_utils/stream/stream.h +++ b/src/library/string_utils/stream/stream.h @@ -1,4 +1,4 @@ -#include +#include namespace NUtils { diff --git a/src/library/string_utils/url/CMakeLists.txt b/src/library/string_utils/url/CMakeLists.txt index c446c9284a7..e9d40ef9fee 100644 --- a/src/library/string_utils/url/CMakeLists.txt +++ b/src/library/string_utils/url/CMakeLists.txt @@ -1,8 +1,10 @@ add_library(string_utils-url) -target_link_libraries(string_utils-url PUBLIC - yutil - string_utils-misc +target_link_libraries(string_utils-url + PUBLIC + yutil + PRIVATE + string_utils-misc ) target_sources(string_utils-url PRIVATE diff --git a/src/library/string_utils/url/url.cpp b/src/library/string_utils/url/url.cpp index 06002e2f6ed..3a5415a8a5d 100644 --- a/src/library/string_utils/url/url.cpp +++ b/src/library/string_utils/url/url.cpp @@ -1,22 +1,22 @@ #include "url.h" -#include // for ToLower -#include -#include +#include // for ToLower +#include +#include #include #include -#include -#include -#include +#include +#include +#include -#include -#include -#include -#include +#include +#include +#include +#include -#include +#include #include diff --git a/src/library/string_utils/url/url.h b/src/library/string_utils/url/url.h index 8457dc562bd..71e144a8856 100644 --- a/src/library/string_utils/url/url.h +++ b/src/library/string_utils/url/url.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include namespace NUrl { diff --git a/src/library/threading/CMakeLists.txt b/src/library/threading/CMakeLists.txt index 90770076c9f..cad72b4ac40 100644 --- a/src/library/threading/CMakeLists.txt +++ b/src/library/threading/CMakeLists.txt @@ -2,4 +2,3 @@ add_subdirectory(chunk_queue) add_subdirectory(equeue) add_subdirectory(future) add_subdirectory(light_rw_lock) -add_subdirectory(poor_man_openmp) diff --git a/src/library/threading/poor_man_openmp/CMakeLists.txt b/src/library/threading/poor_man_openmp/CMakeLists.txt deleted file mode 100644 index 0233f244273..00000000000 --- a/src/library/threading/poor_man_openmp/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -add_library(threading-poor_man_openmp) - -target_link_libraries(threading-poor_man_openmp PUBLIC - yutil - deprecated-atomic -) - -target_sources(threading-poor_man_openmp PRIVATE - ${YDB_SDK_SOURCE_DIR}/src/library/threading/poor_man_openmp/thread_helper.cpp -) - -_ydb_sdk_install_targets(TARGETS threading-poor_man_openmp) diff --git a/src/library/threading/poor_man_openmp/thread_helper.cpp b/src/library/threading/poor_man_openmp/thread_helper.cpp deleted file mode 100644 index b694aee51ed..00000000000 --- a/src/library/threading/poor_man_openmp/thread_helper.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "thread_helper.h" - -#include - -TMtpQueueHelper& TMtpQueueHelper::Instance() { - return *Singleton(); -} diff --git a/src/library/threading/poor_man_openmp/thread_helper.h b/src/library/threading/poor_man_openmp/thread_helper.h deleted file mode 100644 index 6ea6f484f4e..00000000000 --- a/src/library/threading/poor_man_openmp/thread_helper.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -class TMtpQueueHelper { -public: - TMtpQueueHelper() { - SetThreadCount(NSystemInfo::CachedNumberOfCpus()); - } - IThreadPool* Get() { - return q.get(); - } - size_t GetThreadCount() { - return ThreadCount; - } - void SetThreadCount(size_t threads) { - ThreadCount = threads; - q = CreateThreadPool(ThreadCount); - } - - static TMtpQueueHelper& Instance(); - -private: - size_t ThreadCount; - std::unique_ptr q; -}; - -namespace NYmp { - inline void SetThreadCount(size_t threads) { - TMtpQueueHelper::Instance().SetThreadCount(threads); - } - - inline size_t GetThreadCount() { - return TMtpQueueHelper::Instance().GetThreadCount(); - } - - template - inline void ParallelForStaticChunk(T begin, T end, size_t chunkSize, std::function func) { - chunkSize = Max(chunkSize, 1); - - size_t threadCount = TMtpQueueHelper::Instance().GetThreadCount(); - IThreadPool* queue = TMtpQueueHelper::Instance().Get(); - std::condition_variable cv; - std::mutex mutex; - TAtomic counter = threadCount; - std::exception_ptr err; - - for (size_t i = 0; i < threadCount; ++i) { - queue->SafeAddFunc([&cv, &counter, &mutex, &func, i, begin, end, chunkSize, threadCount, &err]() { - try { - T currentChunkStart = begin + static_cast(i * chunkSize); - - while (currentChunkStart < end) { - T currentChunkEnd = Min(end, currentChunkStart + chunkSize); - - for (T val = currentChunkStart; val < currentChunkEnd; ++val) { - func(val); - } - - currentChunkStart += chunkSize * threadCount; - } - } catch (...) { - std::lock_guard guard(mutex); - err = std::current_exception(); - } - - std::lock_guard guard(mutex); - if (AtomicDecrement(counter) == 0) { - //last one - cv.notify_one(); - } - }); - } - - { - std::unique_lock lock(mutex); - while (AtomicGet(counter) > 0) { - cv.wait(lock); - } - } - - if (err) { - std::rethrow_exception(err); - } - } - - template - inline void ParallelForStaticAutoChunk(T begin, T end, std::function func) { - const size_t taskSize = end - begin; - const size_t threadCount = TMtpQueueHelper::Instance().GetThreadCount(); - - ParallelForStaticChunk(begin, end, (taskSize + threadCount - 1) / threadCount, func); - } -} diff --git a/src/library/threading/poor_man_openmp/thread_helper_ut.cpp b/src/library/threading/poor_man_openmp/thread_helper_ut.cpp deleted file mode 100644 index f788e643621..00000000000 --- a/src/library/threading/poor_man_openmp/thread_helper_ut.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "thread_helper.h" - -#include - -#include -#include - -Y_UNIT_TEST_SUITE(TestMP) { - Y_UNIT_TEST(TestErr) { - std::function f = [](int x) { - if (x == 5) { - ythrow yexception() << "oops"; - } - }; - - std::string s; - - try { - NYmp::ParallelForStaticAutoChunk(0, 10, f); - } catch (...) { - s = CurrentExceptionMessage(); - } - - UNIT_ASSERT(s.find("oops") > 0); - } -} diff --git a/src/library/threading/poor_man_openmp/ut/ya.make b/src/library/threading/poor_man_openmp/ut/ya.make deleted file mode 100644 index 8ef7805b8e2..00000000000 --- a/src/library/threading/poor_man_openmp/ut/ya.make +++ /dev/null @@ -1,7 +0,0 @@ -UNITTEST_FOR(src/library/threading/poor_man_openmp) - -SRCS( - thread_helper_ut.cpp -) - -END() diff --git a/tests/some_code.cpp b/tests/some_code.cpp new file mode 100644 index 00000000000..24735bf05a3 --- /dev/null +++ b/tests/some_code.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#include + +int main() { + TString s; + TDeque d; + + Cin >> s; + + s += "abacaba"; + d.push_back(s); + + Cout << s; + return 0; +} \ No newline at end of file diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 312326396c4..07482f25f2c 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -2,3 +2,13 @@ add_subdirectory(client) add_subdirectory(json_value) add_subdirectory(library) add_subdirectory(util) + +add_executable(some_code) + +target_sources(some_code PRIVATE + some_code.cpp +) + +target_link_libraries(some_code PRIVATE + yutil +) diff --git a/tools/enum_parser/enum_serialization_runtime/CMakeLists.txt b/tools/enum_parser/enum_serialization_runtime/CMakeLists.txt index 4215eb34346..453a102c511 100644 --- a/tools/enum_parser/enum_serialization_runtime/CMakeLists.txt +++ b/tools/enum_parser/enum_serialization_runtime/CMakeLists.txt @@ -8,7 +8,6 @@ target_sources(enum_serialization_runtime PRIVATE ${YDB_SDK_SOURCE_DIR}/tools/enum_parser/enum_serialization_runtime/dispatch_methods.cpp ${YDB_SDK_SOURCE_DIR}/tools/enum_parser/enum_serialization_runtime/enum_runtime.cpp ${YDB_SDK_SOURCE_DIR}/tools/enum_parser/enum_serialization_runtime/ordered_pairs.cpp - ${YDB_SDK_SOURCE_DIR}/tools/enum_parser/enum_serialization_runtime/serialized_enum.cpp ) _ydb_sdk_install_targets(TARGETS enum_serialization_runtime) diff --git a/tools/enum_parser/enum_serialization_runtime/enum_runtime.cpp b/tools/enum_parser/enum_serialization_runtime/enum_runtime.cpp index e0ca62271bd..7440b1dc68e 100644 --- a/tools/enum_parser/enum_serialization_runtime/enum_runtime.cpp +++ b/tools/enum_parser/enum_serialization_runtime/enum_runtime.cpp @@ -1,8 +1,8 @@ #include "enum_runtime.h" -#include +#include -#include +#include namespace NEnumSerializationRuntime { template diff --git a/tools/enum_parser/enum_serialization_runtime/enum_runtime.h b/tools/enum_parser/enum_serialization_runtime/enum_runtime.h index 2a17d60cb3d..d8ba773446d 100644 --- a/tools/enum_parser/enum_serialization_runtime/enum_runtime.h +++ b/tools/enum_parser/enum_serialization_runtime/enum_runtime.h @@ -2,7 +2,7 @@ #include "dispatch_methods.h" #include "ordered_pairs.h" -#include "serialized_enum.h" +#include #include diff --git a/tools/enum_parser/enum_serialization_runtime/ordered_pairs.h b/tools/enum_parser/enum_serialization_runtime/ordered_pairs.h index ec341b54d2e..116faa99acb 100644 --- a/tools/enum_parser/enum_serialization_runtime/ordered_pairs.h +++ b/tools/enum_parser/enum_serialization_runtime/ordered_pairs.h @@ -1,8 +1,8 @@ #pragma once -#include -#include -#include +#include +#include +#include #include #include #include diff --git a/tools/enum_parser/parse_enum/CMakeLists.txt b/tools/enum_parser/parse_enum/CMakeLists.txt index 0f546ec500a..68b9bbdb300 100644 --- a/tools/enum_parser/parse_enum/CMakeLists.txt +++ b/tools/enum_parser/parse_enum/CMakeLists.txt @@ -3,7 +3,6 @@ add_library(tools-enum_parser-parse_enum) target_link_libraries(tools-enum_parser-parse_enum PUBLIC yutil library-cppparser - string_utils-misc ) target_sources(tools-enum_parser-parse_enum PRIVATE diff --git a/tools/enum_parser/parse_enum/parse_enum.cpp b/tools/enum_parser/parse_enum/parse_enum.cpp index 1896455c47c..af828cd7759 100644 --- a/tools/enum_parser/parse_enum/parse_enum.cpp +++ b/tools/enum_parser/parse_enum/parse_enum.cpp @@ -2,9 +2,9 @@ #include -#include -#include -#include +#include +#include +#include #include @@ -139,7 +139,7 @@ class TEnumContext: public TCppFullSax { } void DoMultiLineComment(const TText& text) override { - Y_ENSURE(text.Data.size() >= 4, "Invalid multiline comment " << NUtils::Quote(text.Data) << ". "); + Y_ENSURE(text.Data.size() >= 4, "Invalid multiline comment " << TString(text.Data).Quote() << ". "); std::string commentText = text.Data.substr(2, text.Data.size() - 4); commentText = StripString(commentText); CurrentItem.CommentText = commentText; @@ -288,8 +288,8 @@ class TCppContext: public TCppFullSax { size_t contextOffsetBegin = (offset >= 256) ? offset - 256 : 0; std::string codeContext = std::string(Data + contextOffsetBegin, offset - contextOffsetBegin + 1); ythrow yexception() << "C++ source parse failed: unbalanced scope. Did you miss a closing '}' bracket? " - "Context: enum " << NUtils::Quote(CurrentEnum.CppName) << - " in scope " << NUtils::Quote(TEnumParser::ScopeStr(CurrentEnum.Scope)) << ". Code context:\n... " << + "Context: enum " << TString(CurrentEnum.CppName).Quote() << + " in scope " << TString(TEnumParser::ScopeStr(CurrentEnum.Scope)).Quote() << ". Code context:\n... " << codeContext << " ..."; } Scope.pop_back(); @@ -303,7 +303,7 @@ class TCppContext: public TCppFullSax { std::string ofFile; if (!SourceFileName.empty()) { ofFile += " of file "; - ofFile += NUtils::Quote(SourceFileName); + ofFile += TString(SourceFileName).Quote(); } ythrow yexception() << "Failed to parse enum " << CurrentEnum.CppName << " in scope " << TEnumParser::ScopeStr(CurrentEnum.Scope) << ofFile << @@ -333,7 +333,7 @@ class TCppContext: public TCppFullSax { } void PrintEnum(const TEnum& en) { - std::cerr << "Enum within scope " << NUtils::Quote(TEnumParser::ScopeStr(en.Scope)) << std::endl; + std::cerr << "Enum within scope " << TString(TEnumParser::ScopeStr(en.Scope)).Quote() << std::endl; for (const auto& item : en.Items) { std::cerr << " " << item.CppName; if (item.Value) @@ -396,7 +396,8 @@ void TEnumParser::Parse(const char* dataIn, size_t lengthIn) { std::string line; std::string result; - while (mi.ReadLine(line)) { + while (mi.Avail()) { + line = mi.ReadLine(); if (line.find("if (GetOwningArena() == other->GetOwningArena()) {") == std::string::npos) { result += line; result += "\n"; diff --git a/tools/enum_parser/parse_enum/parse_enum.h b/tools/enum_parser/parse_enum/parse_enum.h index 71671ba9169..31e5071cd19 100644 --- a/tools/enum_parser/parse_enum/parse_enum.h +++ b/tools/enum_parser/parse_enum/parse_enum.h @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include #include #include diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt new file mode 100644 index 00000000000..b20428814c2 --- /dev/null +++ b/util/CMakeLists.txt @@ -0,0 +1,367 @@ +add_subdirectory(charset) +add_subdirectory(draft) + +add_library(yutil) + +target_compile_options(yutil PUBLIC + -DTSTRING_IS_STD_STRING +) + +target_compile_options(yutil PRIVATE + -Wnarrowing + -DNO_CUSTOM_CHAR_PTR_STD_COMPARATOR +) + +target_link_libraries(yutil PUBLIC + util-charset-old + contrib-libs-libc_compat + ZLIB::ZLIB + double-conversion::double-conversion +) + +target_joined_source(yutil + all_datetime.cpp + ${YDB_SDK_SOURCE_DIR}/util/datetime/base.cpp + ${YDB_SDK_SOURCE_DIR}/util/datetime/constants.cpp + ${YDB_SDK_SOURCE_DIR}/util/datetime/cputimer.cpp + ${YDB_SDK_SOURCE_DIR}/util/datetime/systime.cpp + ${YDB_SDK_SOURCE_DIR}/util/datetime/uptime.cpp +) + +target_joined_source(yutil + all_digest.cpp + ${YDB_SDK_SOURCE_DIR}/util/digest/fnv.cpp + ${YDB_SDK_SOURCE_DIR}/util/digest/multi.cpp + ${YDB_SDK_SOURCE_DIR}/util/digest/murmur.cpp + ${YDB_SDK_SOURCE_DIR}/util/digest/numeric.cpp + ${YDB_SDK_SOURCE_DIR}/util/digest/sequence.cpp +) + +target_joined_source(yutil + all_util.cpp + ${YDB_SDK_SOURCE_DIR}/util/ysafeptr.cpp + ${YDB_SDK_SOURCE_DIR}/util/ysaveload.cpp + ${YDB_SDK_SOURCE_DIR}/util/str_stl.cpp +) + +target_joined_source(yutil + all_folder.cpp + ${YDB_SDK_SOURCE_DIR}/util/folder/dirut.cpp + ${YDB_SDK_SOURCE_DIR}/util/folder/filelist.cpp + ${YDB_SDK_SOURCE_DIR}/util/folder/fts.cpp + ${YDB_SDK_SOURCE_DIR}/util/folder/fwd.cpp + ${YDB_SDK_SOURCE_DIR}/util/folder/iterator.cpp + ${YDB_SDK_SOURCE_DIR}/util/folder/path.cpp + ${YDB_SDK_SOURCE_DIR}/util/folder/pathsplit.cpp + ${YDB_SDK_SOURCE_DIR}/util/folder/tempdir.cpp +) + +target_joined_source(yutil + all_generic.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/adaptor.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/algorithm.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/array_ref.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/array_size.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/bitmap.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/bitops.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/bt_exception.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/buffer.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/cast.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/deque.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/explicit_type.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/fastqueue.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/flags.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/function.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/function_ref.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/fwd.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/guid.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/hash.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/hash_multi_map.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/hash_table.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/hash_primes.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/hash_set.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/hide_ptr.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/intrlist.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/is_in.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/iterator.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/iterator_range.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/lazy_value.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/list.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/map.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/mapfindptr.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/maybe.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/mem_copy.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/noncopyable.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/object_counter.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/overloaded.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/ptr.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/queue.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/refcount.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/scope.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/serialized_enum.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/set.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/singleton.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/size_literals.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/stack.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/store_policy.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/strbuf.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/strfcpy.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/string.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/typelist.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/typetraits.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/utility.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/va_args.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/variant.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/vector.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/xrange.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/yexception.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/ylimits.cpp + ${YDB_SDK_SOURCE_DIR}/util/generic/ymath.cpp +) + +target_joined_source(yutil + all_memory.cpp + ${YDB_SDK_SOURCE_DIR}/util/memory/addstorage.cpp + ${YDB_SDK_SOURCE_DIR}/util/memory/alloc.cpp + ${YDB_SDK_SOURCE_DIR}/util/memory/blob.cpp + ${YDB_SDK_SOURCE_DIR}/util/memory/mmapalloc.cpp + ${YDB_SDK_SOURCE_DIR}/util/memory/pool.cpp + ${YDB_SDK_SOURCE_DIR}/util/memory/segmented_string_pool.cpp + ${YDB_SDK_SOURCE_DIR}/util/memory/segpool_alloc.cpp + ${YDB_SDK_SOURCE_DIR}/util/memory/smallobj.cpp + ${YDB_SDK_SOURCE_DIR}/util/memory/tempbuf.cpp +) + +target_joined_source(yutil + all_network.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/address.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/endpoint.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/hostip.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/init.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/interface.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/iovec.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/ip.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/nonblock.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/pair.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/poller.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/pollerimpl.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/sock.cpp + ${YDB_SDK_SOURCE_DIR}/util/network/socket.cpp +) + +target_joined_source(yutil + all_random.cpp + ${YDB_SDK_SOURCE_DIR}/util/random/common_ops.cpp + ${YDB_SDK_SOURCE_DIR}/util/random/easy.cpp + ${YDB_SDK_SOURCE_DIR}/util/random/entropy.cpp + ${YDB_SDK_SOURCE_DIR}/util/random/fast.cpp + ${YDB_SDK_SOURCE_DIR}/util/random/lcg_engine.cpp + ${YDB_SDK_SOURCE_DIR}/util/random/mersenne32.cpp + ${YDB_SDK_SOURCE_DIR}/util/random/mersenne64.cpp + ${YDB_SDK_SOURCE_DIR}/util/random/mersenne.cpp + ${YDB_SDK_SOURCE_DIR}/util/random/normal.cpp + ${YDB_SDK_SOURCE_DIR}/util/random/shuffle.cpp + ${YDB_SDK_SOURCE_DIR}/util/random/init_atfork.cpp +) + +target_joined_source(yutil + all_stream.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/aligned.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/buffer.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/buffered.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/debug.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/direct_io.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/file.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/format.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/fwd.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/hex.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/holder.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/input.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/labeled.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/length.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/mem.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/multi.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/null.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/output.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/pipe.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/printf.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/str.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/tee.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/tempbuf.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/tokenizer.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/trace.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/walk.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/zerocopy.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/zerocopy_output.cpp + ${YDB_SDK_SOURCE_DIR}/util/stream/zlib.cpp +) + +target_joined_source(yutil + all_string.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/ascii.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/builder.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/cstriter.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/escape.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/hex.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/join.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/printf.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/reverse.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/split.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/strip.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/strspn.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/subst.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/type.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/util.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/vector.cpp +) + +target_joined_source(yutil + all_system_1.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/atexit.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/backtrace.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/compat.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/condvar.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/daemon.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/datetime.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/defaults.c + ${YDB_SDK_SOURCE_DIR}/util/system/direct_io.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/dynlib.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/env.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/error.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/event.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/fasttime.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/file.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/file_lock.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/filemap.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/flock.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/fs.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/fstat.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/getpid.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/hi_lo.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/hostname.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/hp_timer.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/info.cpp +) + +target_joined_source(yutil + all_system_2.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/context.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/execpath.cpp +) + +target_joined_source(yutil + all_system_3.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/align.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/byteorder.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/cpu_id.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/fhandle.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/guard.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/interrupt_signals.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/madvise.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/maxlen.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/mincore.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/mktemp.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/mlock.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/mutex.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/nice.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/pipe.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/platform.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/progname.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/protect.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/rusage.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/rwlock.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/sanitizers.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/shellcommand.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/shmat.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/sigset.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/spinlock.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/spin_wait.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/src_location.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/sys_alloc.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/sysstat.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/tempfile.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/tls.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/type_name.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/unaligned_mem.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/user.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/utime.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/yassert.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/yield.cpp +) + +target_joined_source(yutil + all_system_4.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/mem_info.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/sem.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/thread.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/types.cpp +) + +target_joined_source(yutil + all_thread.cpp + ${YDB_SDK_SOURCE_DIR}/util/thread/factory.cpp + ${YDB_SDK_SOURCE_DIR}/util/thread/fwd.cpp + ${YDB_SDK_SOURCE_DIR}/util/thread/lfqueue.cpp + ${YDB_SDK_SOURCE_DIR}/util/thread/lfstack.cpp + ${YDB_SDK_SOURCE_DIR}/util/thread/pool.cpp + ${YDB_SDK_SOURCE_DIR}/util/thread/singleton.cpp +) + +target_sources(yutil PRIVATE + ${YDB_SDK_SOURCE_DIR}/util/system/compiler.cpp + ${YDB_SDK_SOURCE_DIR}/util/digest/city.cpp + ${YDB_SDK_SOURCE_DIR}/util/random/random.cpp + ${YDB_SDK_SOURCE_DIR}/util/string/cast.cpp +) + +if (NOT WIN32) + target_ragel_lexers(yutil + PRIVATE + ${YDB_SDK_SOURCE_DIR}/util/datetime/parser.rl6 + -CG2 + ) +endif () + +if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + target_yasm_source(yutil + PRIVATE + ${YDB_SDK_SOURCE_DIR}/util/system/context_x86.asm + -I + ${YDB_SDK_BINARY_DIR} + -I + ${YDB_SDK_SOURCE_DIR} + ) +elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") + target_sources(yutil PRIVATE + ${YDB_SDK_SOURCE_DIR}/util/system/context_aarch64.S + ) +endif () + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_sources(yutil PRIVATE + ${YDB_SDK_SOURCE_DIR}/util/system/valgrind.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/mktemp_system.cpp + ) +elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64") + target_sources(yutil PRIVATE + ${YDB_SDK_SOURCE_DIR}/util/datetime/strptime.cpp + ${YDB_SDK_SOURCE_DIR}/util/folder/lstat_win.c + ${YDB_SDK_SOURCE_DIR}/util/folder/dirent_win.c + ${YDB_SDK_SOURCE_DIR}/util/system/err.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/fs_win.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/winint.cpp + ${YDB_SDK_SOURCE_DIR}/util/system/mktemp_system.cpp + ) + target_ragel_lexers(yutil + PRIVATE + ${YDB_SDK_SOURCE_DIR}/util/datetime/parser.rl6 + -CT0 + ) +endif() + +target_compile_definitions(yutil PUBLIC + ARCADIA_ROOT_CMAKE_HELPER=${YDB_SDK_SOURCE_DIR} + ARCADIA_BUILD_ROOT_CMAKE_HELPER=${YDB_SDK_BINARY_DIR} +) + +_ydb_sdk_install_targets(TARGETS yutil) diff --git a/util/README.md b/util/README.md new file mode 100644 index 00000000000..496efa633c1 --- /dev/null +++ b/util/README.md @@ -0,0 +1,70 @@ +# Coding style + +Style guide for the util folder is a stricter version of +[general style guide](https://docs.yandex-team.ru/arcadia-cpp/cpp_style_guide) +(mostly in terms of ambiguity resolution). + + * all {} must be in K&R style + * &, * tied closer to a type, not to variable + * always use `using` not `typedef` + * even a single line block must be in braces {}: + ``` + if (A) { + B(); + } + ``` + * _ at the end of private data member of a class - `First_`, `Second_` + * every .h file must be accompanied with corresponding .cpp to avoid a leakage and check that it is self contained + * prohibited to use `printf`-like functions + + +Things declared in the general style guide, which sometimes are missed: + + * `template <`, not `template<` + * `noexcept`, not `throw ()` nor `throw()`, not required for destructors + * indents inside `namespace` same as inside `class` + + +Requirements for a new code (and for corrections in an old code which involves change of behaviour) in util: + + * presence of UNIT-tests + * presence of comments in Doxygen style + * accessors without Get prefix (`Length()`, but not `GetLength()`) + +This guide is not a mandatory as there is the general style guide. +Nevertheless if it is not followed, then a next `ya style .` run in the util folder will undeservedly update authors of some lines of code. + +Thus before a commit it is recommended to run `ya style .` in the util folder. + + +Don't forget to run tests from folder `tests`: `ya make -t tests` + +**Note:** tests are designed to run using `autocheck/` solution. + +# Submitting a patch + +In order to make a commit, you have to get approval from one of +[util](https://arcanum.yandex-team.ru/arc/trunk/arcadia/groups/util) members. + +If no comments have been received withing 1–2 days, it is OK +to send a graceful ping into [Igni et ferro](https://wiki.yandex-team.ru/ignietferro/) chat. + +Certain exceptions apply. The following trivial changes do not need to be reviewed: + +* docs, comments, typo fixes, +* renaming of an internal variable to match the styleguide. + +Whenever a breaking change happens to accidentally land into trunk, reverting it does not need to be reviewed. + +## Stale/abandoned review request policy + +Sometimes review requests are neither merged nor discarded, and stay in review request queue forever. +To limit the incoming review request queue size, util reviewers follow these rules: + +- A review request is considered stale if it is not updated by its author for at least 3 months, or if its author has left Yandex. +- A stale review request may be marked as discarded by util reviewers. + +Review requests discarded as stale may be reopened or resubmitted by any committer willing to push them to completion. + +**Note:** It's an author's duty to push the review request to completion. +If util reviewers stop responding to updates, they should be politely pinged via appropriate means of communication. diff --git a/util/charset/CMakeLists.txt b/util/charset/CMakeLists.txt new file mode 100644 index 00000000000..8249533a3d5 --- /dev/null +++ b/util/charset/CMakeLists.txt @@ -0,0 +1,35 @@ +add_library(util-charset-old) + +target_joined_source(util-charset-old + all_charset.cpp + ${YDB_SDK_SOURCE_DIR}/util/charset/generated/unidata.cpp + ${YDB_SDK_SOURCE_DIR}/util/charset/recode_result.cpp + ${YDB_SDK_SOURCE_DIR}/util/charset/unicode_table.cpp + ${YDB_SDK_SOURCE_DIR}/util/charset/unidata.cpp + ${YDB_SDK_SOURCE_DIR}/util/charset/utf8.cpp + ${YDB_SDK_SOURCE_DIR}/util/charset/wide.cpp +) + +if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64") + target_sources_custom(util-charset-old + .sse41 + SRCS + ${YDB_SDK_SOURCE_DIR}/util/charset/wide_sse41.cpp + CUSTOM_FLAGS + -msse4.1 + ) +elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") + target_sources(util-charset-old PRIVATE + ${YDB_SDK_SOURCE_DIR}/util/charset/wide_sse41.cpp + ) + set_property( + SOURCE + ${YDB_SDK_SOURCE_DIR}/util/charset/wide_sse41.cpp + APPEND + PROPERTY + COMPILE_OPTIONS + -DSSE41_STUB + ) +endif() + +_ydb_sdk_install_targets(TARGETS util-charset-old) diff --git a/util/charset/generated/unidata.cpp b/util/charset/generated/unidata.cpp new file mode 100644 index 00000000000..6f5adbbc0aa --- /dev/null +++ b/util/charset/generated/unidata.cpp @@ -0,0 +1,7538 @@ +#include + +namespace { namespace NUnidataTableGenerated { + using TV = const NUnicode::NPrivate::TUnidataTable::TStored; + + static const TV V[] = { +#undef V0 +#define V0 (*(V + 0)) + {0xF018, 0, 0, 0, 0}, +#undef V1 +#define V1 (*(V + 1)) + {0x9F019, 0, 0, 0, 0}, +#undef V2 +#define V2 (*(V + 2)) + {0x8F019, 0, 0, 0, 0}, +#undef V3 +#define V3 (*(V + 3)) + {0x8F01A, 0, 0, 0, 0}, +#undef V4 +#define V4 (*(V + 4)) + {0x9F01A, 0, 0, 0, 0}, +#undef V5 +#define V5 (*(V + 5)) + {0xAF014, 0, 0, 0, 0}, +#undef V6 +#define V6 (*(V + 6)) + {0xBF02C, 0, 0, 0, 0}, +#undef V7 +#define V7 (*(V + 7)) + {0xBF02B, 0, 0, 0, 0}, +#undef V8 +#define V8 (*(V + 8)) + {0x5F02A, 0, 0, 0, 0}, +#undef V9 +#define V9 (*(V + 9)) + {0x5F031, 0, 0, 0, 0}, +#undef V10 +#define V10 (*(V + 10)) + {0xBF02A, 0, 0, 0, 0}, +#undef V11 +#define V11 (*(V + 11)) + {0xBF038, 0, 0, 0, 0}, +#undef V12 +#define V12 (*(V + 12)) + {0xBF023, 0, 0, 0, 0}, +#undef V13 +#define V13 (*(V + 13)) + {0xBF025, 0, 0, 0, 0}, +#undef V14 +#define V14 (*(V + 14)) + {0x4F02F, 0, 0, 0, 0}, +#undef V15 +#define V15 (*(V + 15)) + {0x7F02C, 0, 0, 0, 0}, +#undef V16 +#define V16 (*(V + 16)) + {0x4F022, 0, 0, 0, 0}, +#undef V17 +#define V17 (*(V + 17)) + {0x7F02A, 0, 0, 0, 0}, +#undef V18 +#define V18 (*(V + 18)) + {0x3F0D0, 0, 0, 0, 0}, +#undef V19 +#define V19 (*(V + 19)) + {0x43F0D0, 0, 0, 0, 0}, +#undef V20 +#define V20 (*(V + 20)) + {0x83F0D0, 0, 0, 0, 0}, +#undef V21 +#define V21 (*(V + 21)) + {0xC3F0D0, 0, 0, 0, 0}, +#undef V22 +#define V22 (*(V + 22)) + {0x103F0D0, 0, 0, 0, 0}, +#undef V23 +#define V23 (*(V + 23)) + {0x143F0D0, 0, 0, 0, 0}, +#undef V24 +#define V24 (*(V + 24)) + {0x183F0D0, 0, 0, 0, 0}, +#undef V25 +#define V25 (*(V + 25)) + {0x1C3F0D0, 0, 0, 0, 0}, +#undef V26 +#define V26 (*(V + 26)) + {0x203F0D0, 0, 0, 0, 0}, +#undef V27 +#define V27 (*(V + 27)) + {0x243F0D0, 0, 0, 0, 0}, +#undef V28 +#define V28 (*(V + 28)) + {0xBF02F, 0, 0, 0, 0}, +#undef V29 +#define V29 (*(V + 29)) + {0x1F041, 32, 0, 0, 0}, +#undef V30 +#define V30 (*(V + 30)) + {0x1F001, 32, 0, 0, 0}, +#undef V31 +#define V31 (*(V + 31)) + {0xBF032, 0, 0, 0, 0}, +#undef V32 +#define V32 (*(V + 32)) + {0xBF029, 0, 0, 0, 0}, +#undef V33 +#define V33 (*(V + 33)) + {0x1F042, 0, -32, -32, 0}, +#undef V34 +#define V34 (*(V + 34)) + {0x1F002, 0, -32, -32, 0}, +#undef V35 +#define V35 (*(V + 35)) + {0x8F018, 0, 0, 0, 0}, +#undef V36 +#define V36 (*(V + 36)) + {0x73514, 0, 0, 0, 0}, +#undef V37 +#define V37 (*(V + 37)) + {0xBF033, 0, 0, 0, 0}, +#undef V38 +#define V38 (*(V + 38)) + {0xB3432, 0, 0, 0, 0}, +#undef V39 +#define V39 (*(V + 39)) + {0x13406, 0, 0, 0, 0}, +#undef V40 +#define V40 (*(V + 40)) + {0xBF027, 0, 0, 0, 0}, +#undef V41 +#define V41 (*(V + 41)) + {0xF01B, 0, 0, 0, 0}, +#undef V42 +#define V42 (*(V + 42)) + {0x5F033, 0, 0, 0, 0}, +#undef V43 +#define V43 (*(V + 43)) + {0x5F02F, 0, 0, 0, 0}, +#undef V44 +#define V44 (*(V + 44)) + {0x833493, 0, 0, 0, 0}, +#undef V45 +#define V45 (*(V + 45)) + {0xC33493, 0, 0, 0, 0}, +#undef V46 +#define V46 (*(V + 46)) + {0x13402, 0, 743, 743, 0}, +#undef V47 +#define V47 (*(V + 47)) + {0xBF02D, 0, 0, 0, 0}, +#undef V48 +#define V48 (*(V + 48)) + {0x433493, 0, 0, 0, 0}, +#undef V49 +#define V49 (*(V + 49)) + {0xBF028, 0, 0, 0, 0}, +#undef V50 +#define V50 (*(V + 50)) + {0xB3413, 0, 0, 0, 0}, +#undef V51 +#define V51 (*(V + 51)) + {0x1A801, 32, 0, 0, 0}, +#undef V52 +#define V52 (*(V + 52)) + {0x1F002, 0, 0, 0, 0}, +#undef V53 +#define V53 (*(V + 53)) + {0x1A802, 0, -32, -32, 0}, +#undef V54 +#define V54 (*(V + 54)) + {0x1A802, 0, 121, 121, 0}, +#undef V55 +#define V55 (*(V + 55)) + {0x1A801, 1, 0, 0, 0}, +#undef V56 +#define V56 (*(V + 56)) + {0x1A802, 0, -1, -1, 0}, +#undef V57 +#define V57 (*(V + 57)) + {0x1F001, 1, 0, 0, 0}, +#undef V58 +#define V58 (*(V + 58)) + {0x1F002, 0, -1, -1, 0}, +#undef V59 +#define V59 (*(V + 59)) + {0x1A801, -199, 0, 0, 0}, +#undef V60 +#define V60 (*(V + 60)) + {0x1F002, 0, -232, -232, 0}, +#undef V61 +#define V61 (*(V + 61)) + {0x13401, 1, 0, 0, 0}, +#undef V62 +#define V62 (*(V + 62)) + {0x13402, 0, -1, -1, 0}, +#undef V63 +#define V63 (*(V + 63)) + {0x13402, 0, 0, 0, 0}, +#undef V64 +#define V64 (*(V + 64)) + {0x1A801, -121, 0, 0, 0}, +#undef V65 +#define V65 (*(V + 65)) + {0x13402, 0, -300, -300, 0}, +#undef V66 +#define V66 (*(V + 66)) + {0x1F002, 0, 195, 195, 0}, +#undef V67 +#define V67 (*(V + 67)) + {0x1F001, 210, 0, 0, 0}, +#undef V68 +#define V68 (*(V + 68)) + {0x1F001, 206, 0, 0, 0}, +#undef V69 +#define V69 (*(V + 69)) + {0x1F001, 205, 0, 0, 0}, +#undef V70 +#define V70 (*(V + 70)) + {0x1F001, 79, 0, 0, 0}, +#undef V71 +#define V71 (*(V + 71)) + {0x1F001, 202, 0, 0, 0}, +#undef V72 +#define V72 (*(V + 72)) + {0x1F001, 203, 0, 0, 0}, +#undef V73 +#define V73 (*(V + 73)) + {0x1F001, 207, 0, 0, 0}, +#undef V74 +#define V74 (*(V + 74)) + {0x1F002, 0, 97, 97, 0}, +#undef V75 +#define V75 (*(V + 75)) + {0x1F001, 211, 0, 0, 0}, +#undef V76 +#define V76 (*(V + 76)) + {0x1F001, 209, 0, 0, 0}, +#undef V77 +#define V77 (*(V + 77)) + {0x1F002, 0, 163, 163, 0}, +#undef V78 +#define V78 (*(V + 78)) + {0x1F001, 213, 0, 0, 0}, +#undef V79 +#define V79 (*(V + 79)) + {0x1F002, 0, 130, 130, 0}, +#undef V80 +#define V80 (*(V + 80)) + {0x1F001, 214, 0, 0, 0}, +#undef V81 +#define V81 (*(V + 81)) + {0x1F001, 218, 0, 0, 0}, +#undef V82 +#define V82 (*(V + 82)) + {0x1F001, 217, 0, 0, 0}, +#undef V83 +#define V83 (*(V + 83)) + {0x1F001, 219, 0, 0, 0}, +#undef V84 +#define V84 (*(V + 84)) + {0x1F006, 0, 0, 0, 0}, +#undef V85 +#define V85 (*(V + 85)) + {0x1F002, 0, 56, 56, 0}, +#undef V86 +#define V86 (*(V + 86)) + {0x13401, 2, 0, 1, 0}, +#undef V87 +#define V87 (*(V + 87)) + {0x13403, 1, -1, 0, 0}, +#undef V88 +#define V88 (*(V + 88)) + {0x13402, 0, -2, -1, 0}, +#undef V89 +#define V89 (*(V + 89)) + {0x1F002, 0, -79, -79, 0}, +#undef V90 +#define V90 (*(V + 90)) + {0x1A802, 0, 0, 0, 0}, +#undef V91 +#define V91 (*(V + 91)) + {0x1F001, -97, 0, 0, 0}, +#undef V92 +#define V92 (*(V + 92)) + {0x1F001, -56, 0, 0, 0}, +#undef V93 +#define V93 (*(V + 93)) + {0x1F001, -130, 0, 0, 0}, +#undef V94 +#define V94 (*(V + 94)) + {0x1F001, 10795, 0, 0, 0}, +#undef V95 +#define V95 (*(V + 95)) + {0x1F001, -163, 0, 0, 0}, +#undef V96 +#define V96 (*(V + 96)) + {0x1F001, 10792, 0, 0, 0}, +#undef V97 +#define V97 (*(V + 97)) + {0x1F002, 0, 10815, 10815, 0}, +#undef V98 +#define V98 (*(V + 98)) + {0x1F001, -195, 0, 0, 0}, +#undef V99 +#define V99 (*(V + 99)) + {0x1F001, 69, 0, 0, 0}, +#undef V100 +#define V100 (*(V + 100)) + {0x1F001, 71, 0, 0, 0}, +#undef V101 +#define V101 (*(V + 101)) + {0x1F002, 0, 10783, 10783, 0}, +#undef V102 +#define V102 (*(V + 102)) + {0x1F002, 0, 10780, 10780, 0}, +#undef V103 +#define V103 (*(V + 103)) + {0x1F002, 0, 10782, 10782, 0}, +#undef V104 +#define V104 (*(V + 104)) + {0x1F002, 0, -210, -210, 0}, +#undef V105 +#define V105 (*(V + 105)) + {0x1F002, 0, -206, -206, 0}, +#undef V106 +#define V106 (*(V + 106)) + {0x1F002, 0, -205, -205, 0}, +#undef V107 +#define V107 (*(V + 107)) + {0x1F002, 0, -202, -202, 0}, +#undef V108 +#define V108 (*(V + 108)) + {0x1F002, 0, -203, -203, 0}, +#undef V109 +#define V109 (*(V + 109)) + {0x1F002, 0, 42319, 42319, 0}, +#undef V110 +#define V110 (*(V + 110)) + {0x1F002, 0, 42315, 42315, 0}, +#undef V111 +#define V111 (*(V + 111)) + {0x1F002, 0, -207, -207, 0}, +#undef V112 +#define V112 (*(V + 112)) + {0x1F002, 0, 42280, 42280, 0}, +#undef V113 +#define V113 (*(V + 113)) + {0x1F002, 0, 42308, 42308, 0}, +#undef V114 +#define V114 (*(V + 114)) + {0x1F002, 0, -209, -209, 0}, +#undef V115 +#define V115 (*(V + 115)) + {0x1F002, 0, -211, -211, 0}, +#undef V116 +#define V116 (*(V + 116)) + {0x1F002, 0, 10743, 10743, 0}, +#undef V117 +#define V117 (*(V + 117)) + {0x1F002, 0, 42305, 42305, 0}, +#undef V118 +#define V118 (*(V + 118)) + {0x1F002, 0, 10749, 10749, 0}, +#undef V119 +#define V119 (*(V + 119)) + {0x1F002, 0, -213, -213, 0}, +#undef V120 +#define V120 (*(V + 120)) + {0x1F002, 0, -214, -214, 0}, +#undef V121 +#define V121 (*(V + 121)) + {0x1F002, 0, 10727, 10727, 0}, +#undef V122 +#define V122 (*(V + 122)) + {0x1F002, 0, -218, -218, 0}, +#undef V123 +#define V123 (*(V + 123)) + {0x1F002, 0, 42282, 42282, 0}, +#undef V124 +#define V124 (*(V + 124)) + {0x1F002, 0, -69, -69, 0}, +#undef V125 +#define V125 (*(V + 125)) + {0x1F002, 0, -217, -217, 0}, +#undef V126 +#define V126 (*(V + 126)) + {0x1F002, 0, -71, -71, 0}, +#undef V127 +#define V127 (*(V + 127)) + {0x1F002, 0, -219, -219, 0}, +#undef V128 +#define V128 (*(V + 128)) + {0x1F002, 0, 42261, 42261, 0}, +#undef V129 +#define V129 (*(V + 129)) + {0x1F002, 0, 42258, 42258, 0}, +#undef V130 +#define V130 (*(V + 130)) + {0x13405, 0, 0, 0, 0}, +#undef V131 +#define V131 (*(V + 131)) + {0xBF005, 0, 0, 0, 0}, +#undef V132 +#define V132 (*(V + 132)) + {0x1F005, 0, 0, 0, 0}, +#undef V133 +#define V133 (*(V + 133)) + {0xBF004, 0, 0, 0, 0}, +#undef V134 +#define V134 (*(V + 134)) + {0x1F004, 0, 0, 0, 0}, +#undef V135 +#define V135 (*(V + 135)) + {0x500D, 0, 0, 0, 230}, +#undef V136 +#define V136 (*(V + 136)) + {0xF00D, 0, 0, 0, 230}, +#undef V137 +#define V137 (*(V + 137)) + {0xF00D, 0, 0, 0, 232}, +#undef V138 +#define V138 (*(V + 138)) + {0xF00D, 0, 0, 0, 220}, +#undef V139 +#define V139 (*(V + 139)) + {0x500D, 0, 0, 0, 216}, +#undef V140 +#define V140 (*(V + 140)) + {0xF00D, 0, 0, 0, 202}, +#undef V141 +#define V141 (*(V + 141)) + {0x500D, 0, 0, 0, 220}, +#undef V142 +#define V142 (*(V + 142)) + {0x500D, 0, 0, 0, 202}, +#undef V143 +#define V143 (*(V + 143)) + {0xF00D, 0, 0, 0, 1}, +#undef V144 +#define V144 (*(V + 144)) + {0x500D, 0, 0, 0, 1}, +#undef V145 +#define V145 (*(V + 145)) + {0x80D, 0, 0, 0, 230}, +#undef V146 +#define V146 (*(V + 146)) + {0x500D, 0, 84, 84, 240}, +#undef V147 +#define V147 (*(V + 147)) + {0xF00D, 0, 0, 0, 0}, +#undef V148 +#define V148 (*(V + 148)) + {0xF00D, 0, 0, 0, 233}, +#undef V149 +#define V149 (*(V + 149)) + {0xF00D, 0, 0, 0, 234}, +#undef V150 +#define V150 (*(V + 150)) + {0xB0804, 0, 0, 0, 0}, +#undef V151 +#define V151 (*(V + 151)) + {0xF000, 0, 0, 0, 0}, +#undef V152 +#define V152 (*(V + 152)) + {0x13404, 0, 0, 0, 0}, +#undef V153 +#define V153 (*(V + 153)) + {0xB082C, 0, 0, 0, 0}, +#undef V154 +#define V154 (*(V + 154)) + {0x1F001, 116, 0, 0, 0}, +#undef V155 +#define V155 (*(V + 155)) + {0xB2832, 0, 0, 0, 0}, +#undef V156 +#define V156 (*(V + 156)) + {0x1A801, 38, 0, 0, 0}, +#undef V157 +#define V157 (*(V + 157)) + {0x1A801, 37, 0, 0, 0}, +#undef V158 +#define V158 (*(V + 158)) + {0x1A801, 64, 0, 0, 0}, +#undef V159 +#define V159 (*(V + 159)) + {0x1A801, 63, 0, 0, 0}, +#undef V160 +#define V160 (*(V + 160)) + {0x1A802, 0, -38, -38, 0}, +#undef V161 +#define V161 (*(V + 161)) + {0x1A802, 0, -37, -37, 0}, +#undef V162 +#define V162 (*(V + 162)) + {0x1F002, 0, -31, -31, 0}, +#undef V163 +#define V163 (*(V + 163)) + {0x1A802, 0, -64, -64, 0}, +#undef V164 +#define V164 (*(V + 164)) + {0x1A802, 0, -63, -63, 0}, +#undef V165 +#define V165 (*(V + 165)) + {0x1F001, 8, 0, 0, 0}, +#undef V166 +#define V166 (*(V + 166)) + {0x13402, 0, -62, -62, 0}, +#undef V167 +#define V167 (*(V + 167)) + {0x13402, 0, -57, -57, 0}, +#undef V168 +#define V168 (*(V + 168)) + {0x13401, 0, 0, 0, 0}, +#undef V169 +#define V169 (*(V + 169)) + {0x12801, 0, 0, 0, 0}, +#undef V170 +#define V170 (*(V + 170)) + {0x13402, 0, -47, -47, 0}, +#undef V171 +#define V171 (*(V + 171)) + {0x13402, 0, -54, -54, 0}, +#undef V172 +#define V172 (*(V + 172)) + {0x1F002, 0, -8, -8, 0}, +#undef V173 +#define V173 (*(V + 173)) + {0x13402, 0, -86, -86, 0}, +#undef V174 +#define V174 (*(V + 174)) + {0x13402, 0, -80, -80, 0}, +#undef V175 +#define V175 (*(V + 175)) + {0x13402, 0, 7, 7, 0}, +#undef V176 +#define V176 (*(V + 176)) + {0x1F002, 0, -116, -116, 0}, +#undef V177 +#define V177 (*(V + 177)) + {0x13401, -60, 0, 0, 0}, +#undef V178 +#define V178 (*(V + 178)) + {0x13402, 0, -96, -96, 0}, +#undef V179 +#define V179 (*(V + 179)) + {0x13401, -7, 0, 0, 0}, +#undef V180 +#define V180 (*(V + 180)) + {0x1A801, 80, 0, 0, 0}, +#undef V181 +#define V181 (*(V + 181)) + {0x1F001, 80, 0, 0, 0}, +#undef V182 +#define V182 (*(V + 182)) + {0x1A802, 0, -80, -80, 0}, +#undef V183 +#define V183 (*(V + 183)) + {0x1F002, 0, -80, -80, 0}, +#undef V184 +#define V184 (*(V + 184)) + {0x1F033, 0, 0, 0, 0}, +#undef V185 +#define V185 (*(V + 185)) + {0xF00E, 0, 0, 0, 0}, +#undef V186 +#define V186 (*(V + 186)) + {0x1F001, 15, 0, 0, 0}, +#undef V187 +#define V187 (*(V + 187)) + {0x1F002, 0, -15, -15, 0}, +#undef V188 +#define V188 (*(V + 188)) + {0x1F001, 48, 0, 0, 0}, +#undef V189 +#define V189 (*(V + 189)) + {0x1F02A, 0, 0, 0, 0}, +#undef V190 +#define V190 (*(V + 190)) + {0x1F002, 0, -48, -48, 0}, +#undef V191 +#define V191 (*(V + 191)) + {0x1F02C, 0, 0, 0, 0}, +#undef V192 +#define V192 (*(V + 192)) + {0xBF022, 0, 0, 0, 0}, +#undef V193 +#define V193 (*(V + 193)) + {0xF00D, 0, 0, 0, 222}, +#undef V194 +#define V194 (*(V + 194)) + {0xF00D, 0, 0, 0, 228}, +#undef V195 +#define V195 (*(V + 195)) + {0xF00D, 0, 0, 0, 10}, +#undef V196 +#define V196 (*(V + 196)) + {0xF00D, 0, 0, 0, 11}, +#undef V197 +#define V197 (*(V + 197)) + {0xF00D, 0, 0, 0, 12}, +#undef V198 +#define V198 (*(V + 198)) + {0xF00D, 0, 0, 0, 13}, +#undef V199 +#define V199 (*(V + 199)) + {0xF00D, 0, 0, 0, 14}, +#undef V200 +#define V200 (*(V + 200)) + {0xF00D, 0, 0, 0, 15}, +#undef V201 +#define V201 (*(V + 201)) + {0xF00D, 0, 0, 0, 16}, +#undef V202 +#define V202 (*(V + 202)) + {0xF00D, 0, 0, 0, 17}, +#undef V203 +#define V203 (*(V + 203)) + {0xF00D, 0, 0, 0, 18}, +#undef V204 +#define V204 (*(V + 204)) + {0xF00D, 0, 0, 0, 19}, +#undef V205 +#define V205 (*(V + 205)) + {0xF00D, 0, 0, 0, 20}, +#undef V206 +#define V206 (*(V + 206)) + {0xF00D, 0, 0, 0, 21}, +#undef V207 +#define V207 (*(V + 207)) + {0xF00D, 0, 0, 0, 22}, +#undef V208 +#define V208 (*(V + 208)) + {0x2F021, 0, 0, 0, 0}, +#undef V209 +#define V209 (*(V + 209)) + {0xF00D, 0, 0, 0, 23}, +#undef V210 +#define V210 (*(V + 210)) + {0x2F02A, 0, 0, 0, 0}, +#undef V211 +#define V211 (*(V + 211)) + {0xF00D, 0, 0, 0, 24}, +#undef V212 +#define V212 (*(V + 212)) + {0xF00D, 0, 0, 0, 25}, +#undef V213 +#define V213 (*(V + 213)) + {0x2F006, 0, 0, 0, 0}, +#undef V214 +#define V214 (*(V + 214)) + {0x6F01B, 0, 0, 0, 0}, +#undef V215 +#define V215 (*(V + 215)) + {0xF02F, 0, 0, 0, 0}, +#undef V216 +#define V216 (*(V + 216)) + {0xF031, 0, 0, 0, 0}, +#undef V217 +#define V217 (*(V + 217)) + {0xF02A, 0, 0, 0, 0}, +#undef V218 +#define V218 (*(V + 218)) + {0xF00D, 0, 0, 0, 30}, +#undef V219 +#define V219 (*(V + 219)) + {0xF00D, 0, 0, 0, 31}, +#undef V220 +#define V220 (*(V + 220)) + {0xF00D, 0, 0, 0, 32}, +#undef V221 +#define V221 (*(V + 221)) + {0xF02C, 0, 0, 0, 0}, +#undef V222 +#define V222 (*(V + 222)) + {0xF006, 0, 0, 0, 0}, +#undef V223 +#define V223 (*(V + 223)) + {0xA806, 0, 0, 0, 0}, +#undef V224 +#define V224 (*(V + 224)) + {0xF004, 0, 0, 0, 0}, +#undef V225 +#define V225 (*(V + 225)) + {0xF00D, 0, 0, 0, 27}, +#undef V226 +#define V226 (*(V + 226)) + {0xF00D, 0, 0, 0, 28}, +#undef V227 +#define V227 (*(V + 227)) + {0xF00D, 0, 0, 0, 29}, +#undef V228 +#define V228 (*(V + 228)) + {0xF00D, 0, 0, 0, 33}, +#undef V229 +#define V229 (*(V + 229)) + {0xF00D, 0, 0, 0, 34}, +#undef V230 +#define V230 (*(V + 230)) + {0x6F090, 0, 0, 0, 0}, +#undef V231 +#define V231 (*(V + 231)) + {0x46F090, 0, 0, 0, 0}, +#undef V232 +#define V232 (*(V + 232)) + {0x86F090, 0, 0, 0, 0}, +#undef V233 +#define V233 (*(V + 233)) + {0xC6F090, 0, 0, 0, 0}, +#undef V234 +#define V234 (*(V + 234)) + {0x106F090, 0, 0, 0, 0}, +#undef V235 +#define V235 (*(V + 235)) + {0x146F090, 0, 0, 0, 0}, +#undef V236 +#define V236 (*(V + 236)) + {0x186F090, 0, 0, 0, 0}, +#undef V237 +#define V237 (*(V + 237)) + {0x1C6F090, 0, 0, 0, 0}, +#undef V238 +#define V238 (*(V + 238)) + {0x206F090, 0, 0, 0, 0}, +#undef V239 +#define V239 (*(V + 239)) + {0x246F090, 0, 0, 0, 0}, +#undef V240 +#define V240 (*(V + 240)) + {0x6F02A, 0, 0, 0, 0}, +#undef V241 +#define V241 (*(V + 241)) + {0xF00D, 0, 0, 0, 35}, +#undef V242 +#define V242 (*(V + 242)) + {0x3406, 0, 0, 0, 0}, +#undef V243 +#define V243 (*(V + 243)) + {0xF005, 0, 0, 0, 0}, +#undef V244 +#define V244 (*(V + 244)) + {0x3F090, 0, 0, 0, 0}, +#undef V245 +#define V245 (*(V + 245)) + {0x43F090, 0, 0, 0, 0}, +#undef V246 +#define V246 (*(V + 246)) + {0x83F090, 0, 0, 0, 0}, +#undef V247 +#define V247 (*(V + 247)) + {0xC3F090, 0, 0, 0, 0}, +#undef V248 +#define V248 (*(V + 248)) + {0x103F090, 0, 0, 0, 0}, +#undef V249 +#define V249 (*(V + 249)) + {0x143F090, 0, 0, 0, 0}, +#undef V250 +#define V250 (*(V + 250)) + {0x183F090, 0, 0, 0, 0}, +#undef V251 +#define V251 (*(V + 251)) + {0x1C3F090, 0, 0, 0, 0}, +#undef V252 +#define V252 (*(V + 252)) + {0x203F090, 0, 0, 0, 0}, +#undef V253 +#define V253 (*(V + 253)) + {0x243F090, 0, 0, 0, 0}, +#undef V254 +#define V254 (*(V + 254)) + {0xF033, 0, 0, 0, 0}, +#undef V255 +#define V255 (*(V + 255)) + {0xF00D, 0, 0, 0, 36}, +#undef V256 +#define V256 (*(V + 256)) + {0x2F090, 0, 0, 0, 0}, +#undef V257 +#define V257 (*(V + 257)) + {0x42F090, 0, 0, 0, 0}, +#undef V258 +#define V258 (*(V + 258)) + {0x82F090, 0, 0, 0, 0}, +#undef V259 +#define V259 (*(V + 259)) + {0xC2F090, 0, 0, 0, 0}, +#undef V260 +#define V260 (*(V + 260)) + {0x102F090, 0, 0, 0, 0}, +#undef V261 +#define V261 (*(V + 261)) + {0x142F090, 0, 0, 0, 0}, +#undef V262 +#define V262 (*(V + 262)) + {0x182F090, 0, 0, 0, 0}, +#undef V263 +#define V263 (*(V + 263)) + {0x1C2F090, 0, 0, 0, 0}, +#undef V264 +#define V264 (*(V + 264)) + {0x202F090, 0, 0, 0, 0}, +#undef V265 +#define V265 (*(V + 265)) + {0x242F090, 0, 0, 0, 0}, +#undef V266 +#define V266 (*(V + 266)) + {0x2F004, 0, 0, 0, 0}, +#undef V267 +#define V267 (*(V + 267)) + {0x2F005, 0, 0, 0, 0}, +#undef V268 +#define V268 (*(V + 268)) + {0x1F00F, 0, 0, 0, 0}, +#undef V269 +#define V269 (*(V + 269)) + {0x1A806, 0, 0, 0, 0}, +#undef V270 +#define V270 (*(V + 270)) + {0x500D, 0, 0, 0, 7}, +#undef V271 +#define V271 (*(V + 271)) + {0xF00D, 0, 0, 0, 9}, +#undef V272 +#define V272 (*(V + 272)) + {0x10806, 0, 0, 0, 0}, +#undef V273 +#define V273 (*(V + 273)) + {0x1F090, 0, 0, 0, 0}, +#undef V274 +#define V274 (*(V + 274)) + {0x41F090, 0, 0, 0, 0}, +#undef V275 +#define V275 (*(V + 275)) + {0x81F090, 0, 0, 0, 0}, +#undef V276 +#define V276 (*(V + 276)) + {0xC1F090, 0, 0, 0, 0}, +#undef V277 +#define V277 (*(V + 277)) + {0x101F090, 0, 0, 0, 0}, +#undef V278 +#define V278 (*(V + 278)) + {0x141F090, 0, 0, 0, 0}, +#undef V279 +#define V279 (*(V + 279)) + {0x181F090, 0, 0, 0, 0}, +#undef V280 +#define V280 (*(V + 280)) + {0x1C1F090, 0, 0, 0, 0}, +#undef V281 +#define V281 (*(V + 281)) + {0x201F090, 0, 0, 0, 0}, +#undef V282 +#define V282 (*(V + 282)) + {0x241F090, 0, 0, 0, 0}, +#undef V283 +#define V283 (*(V + 283)) + {0xF00D, 0, 0, 0, 7}, +#undef V284 +#define V284 (*(V + 284)) + {0x1500F, 0, 0, 0, 0}, +#undef V285 +#define V285 (*(V + 285)) + {0x1A80F, 0, 0, 0, 0}, +#undef V286 +#define V286 (*(V + 286)) + {0x1F013, 0, 0, 0, 0}, +#undef V287 +#define V287 (*(V + 287)) + {0x500D, 0, 0, 0, 0}, +#undef V288 +#define V288 (*(V + 288)) + {0xA80D, 0, 0, 0, 0}, +#undef V289 +#define V289 (*(V + 289)) + {0xF00D, 0, 0, 0, 84}, +#undef V290 +#define V290 (*(V + 290)) + {0x500D, 0, 0, 0, 91}, +#undef V291 +#define V291 (*(V + 291)) + {0xBF013, 0, 0, 0, 0}, +#undef V292 +#define V292 (*(V + 292)) + {0x1F00D, 0, 0, 0, 0}, +#undef V293 +#define V293 (*(V + 293)) + {0x500D, 0, 0, 0, 9}, +#undef V294 +#define V294 (*(V + 294)) + {0xF00D, 0, 0, 0, 103}, +#undef V295 +#define V295 (*(V + 295)) + {0xF00D, 0, 0, 0, 107}, +#undef V296 +#define V296 (*(V + 296)) + {0xF00D, 0, 0, 0, 118}, +#undef V297 +#define V297 (*(V + 297)) + {0xF00D, 0, 0, 0, 122}, +#undef V298 +#define V298 (*(V + 298)) + {0x1342A, 0, 0, 0, 0}, +#undef V299 +#define V299 (*(V + 299)) + {0xF00D, 0, 0, 0, 216}, +#undef V300 +#define V300 (*(V + 300)) + {0xF00D, 0, 0, 0, 129}, +#undef V301 +#define V301 (*(V + 301)) + {0xF00D, 0, 0, 0, 130}, +#undef V302 +#define V302 (*(V + 302)) + {0x80D, 0, 0, 0, 0}, +#undef V303 +#define V303 (*(V + 303)) + {0xF00D, 0, 0, 0, 132}, +#undef V304 +#define V304 (*(V + 304)) + {0x340D, 0, 0, 0, 0}, +#undef V305 +#define V305 (*(V + 305)) + {0x1F001, 7264, 0, 0, 0}, +#undef V306 +#define V306 (*(V + 306)) + {0x1F00A, 0, 0, 0, 0}, +#undef V307 +#define V307 (*(V + 307)) + {0x1F00B, 0, 0, 0, 0}, +#undef V308 +#define V308 (*(V + 308)) + {0x1500B, 0, 0, 0, 0}, +#undef V309 +#define V309 (*(V + 309)) + {0x1500C, 0, 0, 0, 0}, +#undef V310 +#define V310 (*(V + 310)) + {0x1F00C, 0, 0, 0, 0}, +#undef V311 +#define V311 (*(V + 311)) + {0x41F093, 0, 0, 0, 0}, +#undef V312 +#define V312 (*(V + 312)) + {0x81F093, 0, 0, 0, 0}, +#undef V313 +#define V313 (*(V + 313)) + {0xC1F093, 0, 0, 0, 0}, +#undef V314 +#define V314 (*(V + 314)) + {0x101F093, 0, 0, 0, 0}, +#undef V315 +#define V315 (*(V + 315)) + {0x141F093, 0, 0, 0, 0}, +#undef V316 +#define V316 (*(V + 316)) + {0x181F093, 0, 0, 0, 0}, +#undef V317 +#define V317 (*(V + 317)) + {0x1C1F093, 0, 0, 0, 0}, +#undef V318 +#define V318 (*(V + 318)) + {0x201F093, 0, 0, 0, 0}, +#undef V319 +#define V319 (*(V + 319)) + {0x241F093, 0, 0, 0, 0}, +#undef V320 +#define V320 (*(V + 320)) + {0x1F001, 38864, 0, 0, 0}, +#undef V321 +#define V321 (*(V + 321)) + {0x1F011, 0, 0, 0, 0}, +#undef V322 +#define V322 (*(V + 322)) + {0x1F00F, 0, 0, 0, 9}, +#undef V323 +#define V323 (*(V + 323)) + {0x1F002, 0, -6254, -6254, 0}, +#undef V324 +#define V324 (*(V + 324)) + {0x1F002, 0, -6253, -6253, 0}, +#undef V325 +#define V325 (*(V + 325)) + {0x1F002, 0, -6244, -6244, 0}, +#undef V326 +#define V326 (*(V + 326)) + {0x1F002, 0, -6242, -6242, 0}, +#undef V327 +#define V327 (*(V + 327)) + {0x1F002, 0, -6243, -6243, 0}, +#undef V328 +#define V328 (*(V + 328)) + {0x1F002, 0, -6236, -6236, 0}, +#undef V329 +#define V329 (*(V + 329)) + {0x1F002, 0, -6181, -6181, 0}, +#undef V330 +#define V330 (*(V + 330)) + {0x1F002, 0, 35266, 35266, 0}, +#undef V331 +#define V331 (*(V + 331)) + {0x1F002, 0, 35332, 35332, 0}, +#undef V332 +#define V332 (*(V + 332)) + {0x1F002, 0, 3814, 3814, 0}, +#undef V333 +#define V333 (*(V + 333)) + {0xF00D, 0, 0, 0, 214}, +#undef V334 +#define V334 (*(V + 334)) + {0x12802, 0, -59, -59, 0}, +#undef V335 +#define V335 (*(V + 335)) + {0x1F001, -7615, 0, 0, 0}, +#undef V336 +#define V336 (*(V + 336)) + {0x1A802, 0, 8, 8, 0}, +#undef V337 +#define V337 (*(V + 337)) + {0x1A801, -8, 0, 0, 0}, +#undef V338 +#define V338 (*(V + 338)) + {0x1A802, 0, 74, 74, 0}, +#undef V339 +#define V339 (*(V + 339)) + {0x10802, 0, 74, 74, 0}, +#undef V340 +#define V340 (*(V + 340)) + {0x1A802, 0, 86, 86, 0}, +#undef V341 +#define V341 (*(V + 341)) + {0x10802, 0, 86, 86, 0}, +#undef V342 +#define V342 (*(V + 342)) + {0x1A802, 0, 100, 100, 0}, +#undef V343 +#define V343 (*(V + 343)) + {0x10802, 0, 100, 100, 0}, +#undef V344 +#define V344 (*(V + 344)) + {0x1A802, 0, 128, 128, 0}, +#undef V345 +#define V345 (*(V + 345)) + {0x10802, 0, 128, 128, 0}, +#undef V346 +#define V346 (*(V + 346)) + {0x1A802, 0, 112, 112, 0}, +#undef V347 +#define V347 (*(V + 347)) + {0x10802, 0, 112, 112, 0}, +#undef V348 +#define V348 (*(V + 348)) + {0x1A802, 0, 126, 126, 0}, +#undef V349 +#define V349 (*(V + 349)) + {0x10802, 0, 126, 126, 0}, +#undef V350 +#define V350 (*(V + 350)) + {0x1A803, -8, 0, 0, 0}, +#undef V351 +#define V351 (*(V + 351)) + {0x1A802, 0, 9, 9, 0}, +#undef V352 +#define V352 (*(V + 352)) + {0x1A801, -74, 0, 0, 0}, +#undef V353 +#define V353 (*(V + 353)) + {0x10801, -74, 0, 0, 0}, +#undef V354 +#define V354 (*(V + 354)) + {0x1A803, -9, 0, 0, 0}, +#undef V355 +#define V355 (*(V + 355)) + {0x10802, 0, -7205, -7205, 0}, +#undef V356 +#define V356 (*(V + 356)) + {0x1A801, -86, 0, 0, 0}, +#undef V357 +#define V357 (*(V + 357)) + {0x10801, -86, 0, 0, 0}, +#undef V358 +#define V358 (*(V + 358)) + {0x10802, 0, 0, 0, 0}, +#undef V359 +#define V359 (*(V + 359)) + {0x1A801, -100, 0, 0, 0}, +#undef V360 +#define V360 (*(V + 360)) + {0x10801, -100, 0, 0, 0}, +#undef V361 +#define V361 (*(V + 361)) + {0x1A802, 0, 7, 7, 0}, +#undef V362 +#define V362 (*(V + 362)) + {0x1A801, -112, 0, 0, 0}, +#undef V363 +#define V363 (*(V + 363)) + {0x10801, -112, 0, 0, 0}, +#undef V364 +#define V364 (*(V + 364)) + {0x1A801, -7, 0, 0, 0}, +#undef V365 +#define V365 (*(V + 365)) + {0xB0832, 0, 0, 0, 0}, +#undef V366 +#define V366 (*(V + 366)) + {0x1A801, -128, 0, 0, 0}, +#undef V367 +#define V367 (*(V + 367)) + {0x10801, -128, 0, 0, 0}, +#undef V368 +#define V368 (*(V + 368)) + {0x1A801, -126, 0, 0, 0}, +#undef V369 +#define V369 (*(V + 369)) + {0x10801, -126, 0, 0, 0}, +#undef V370 +#define V370 (*(V + 370)) + {0xA0814, 0, 0, 0, 0}, +#undef V371 +#define V371 (*(V + 371)) + {0xA3414, 0, 0, 0, 0}, +#undef V372 +#define V372 (*(V + 372)) + {0xF015, 0, 0, 0, 0}, +#undef V373 +#define V373 (*(V + 373)) + {0xF01C, 0, 0, 0, 0}, +#undef V374 +#define V374 (*(V + 374)) + {0x1F01D, 0, 0, 0, 0}, +#undef V375 +#define V375 (*(V + 375)) + {0x2F01D, 0, 0, 0, 0}, +#undef V376 +#define V376 (*(V + 376)) + {0xB3522, 0, 0, 0, 0}, +#undef V377 +#define V377 (*(V + 377)) + {0xBF021, 0, 0, 0, 0}, +#undef V378 +#define V378 (*(V + 378)) + {0xB342A, 0, 0, 0, 0}, +#undef V379 +#define V379 (*(V + 379)) + {0xBF036, 0, 0, 0, 0}, +#undef V380 +#define V380 (*(V + 380)) + {0xBF037, 0, 0, 0, 0}, +#undef V381 +#define V381 (*(V + 381)) + {0xBF034, 0, 0, 0, 0}, +#undef V382 +#define V382 (*(V + 382)) + {0xBF024, 0, 0, 0, 0}, +#undef V383 +#define V383 (*(V + 383)) + {0xB342C, 0, 0, 0, 0}, +#undef V384 +#define V384 (*(V + 384)) + {0xAF016, 0, 0, 0, 0}, +#undef V385 +#define V385 (*(V + 385)) + {0x8F017, 0, 0, 0, 0}, +#undef V386 +#define V386 (*(V + 386)) + {0xF01D, 0, 0, 0, 0}, +#undef V387 +#define V387 (*(V + 387)) + {0x5F038, 0, 0, 0, 0}, +#undef V388 +#define V388 (*(V + 388)) + {0x5342B, 0, 0, 0, 0}, +#undef V389 +#define V389 (*(V + 389)) + {0xB342B, 0, 0, 0, 0}, +#undef V390 +#define V390 (*(V + 390)) + {0x7F02F, 0, 0, 0, 0}, +#undef V391 +#define V391 (*(V + 391)) + {0x33493, 0, 0, 0, 0}, +#undef V392 +#define V392 (*(V + 392)) + {0x1033493, 0, 0, 0, 0}, +#undef V393 +#define V393 (*(V + 393)) + {0x1433493, 0, 0, 0, 0}, +#undef V394 +#define V394 (*(V + 394)) + {0x1833493, 0, 0, 0, 0}, +#undef V395 +#define V395 (*(V + 395)) + {0x1C33493, 0, 0, 0, 0}, +#undef V396 +#define V396 (*(V + 396)) + {0x2033493, 0, 0, 0, 0}, +#undef V397 +#define V397 (*(V + 397)) + {0x2433493, 0, 0, 0, 0}, +#undef V398 +#define V398 (*(V + 398)) + {0x4342F, 0, 0, 0, 0}, +#undef V399 +#define V399 (*(V + 399)) + {0x43430, 0, 0, 0, 0}, +#undef V400 +#define V400 (*(V + 400)) + {0xB342F, 0, 0, 0, 0}, +#undef V401 +#define V401 (*(V + 401)) + {0xB3423, 0, 0, 0, 0}, +#undef V402 +#define V402 (*(V + 402)) + {0xB3425, 0, 0, 0, 0}, +#undef V403 +#define V403 (*(V + 403)) + {0x53431, 0, 0, 0, 0}, +#undef V404 +#define V404 (*(V + 404)) + {0xB3433, 0, 0, 0, 0}, +#undef V405 +#define V405 (*(V + 405)) + {0x10801, -7517, 0, 0, 0}, +#undef V406 +#define V406 (*(V + 406)) + {0x10801, -8383, 0, 0, 0}, +#undef V407 +#define V407 (*(V + 407)) + {0x10801, -8262, 0, 0, 0}, +#undef V408 +#define V408 (*(V + 408)) + {0x1F001, 28, 0, 0, 0}, +#undef V409 +#define V409 (*(V + 409)) + {0x1F002, 0, -28, -28, 0}, +#undef V410 +#define V410 (*(V + 410)) + {0x13411, 16, 0, 0, 0}, +#undef V411 +#define V411 (*(V + 411)) + {0x13411, 0, -16, -16, 0}, +#undef V412 +#define V412 (*(V + 412)) + {0xBA82F, 0, 0, 0, 0}, +#undef V413 +#define V413 (*(V + 413)) + {0xBA833, 0, 0, 0, 0}, +#undef V414 +#define V414 (*(V + 414)) + {0x4F030, 0, 0, 0, 0}, +#undef V415 +#define V415 (*(V + 415)) + {0xB0823, 0, 0, 0, 0}, +#undef V416 +#define V416 (*(V + 416)) + {0xB0825, 0, 0, 0, 0}, +#undef V417 +#define V417 (*(V + 417)) + {0x4B3493, 0, 0, 0, 0}, +#undef V418 +#define V418 (*(V + 418)) + {0x8B3493, 0, 0, 0, 0}, +#undef V419 +#define V419 (*(V + 419)) + {0xCB3493, 0, 0, 0, 0}, +#undef V420 +#define V420 (*(V + 420)) + {0x10B3493, 0, 0, 0, 0}, +#undef V421 +#define V421 (*(V + 421)) + {0x14B3493, 0, 0, 0, 0}, +#undef V422 +#define V422 (*(V + 422)) + {0x18B3493, 0, 0, 0, 0}, +#undef V423 +#define V423 (*(V + 423)) + {0x1CB3493, 0, 0, 0, 0}, +#undef V424 +#define V424 (*(V + 424)) + {0x20B3493, 0, 0, 0, 0}, +#undef V425 +#define V425 (*(V + 425)) + {0x24B3493, 0, 0, 0, 0}, +#undef V426 +#define V426 (*(V + 426)) + {0x33413, 0, 0, 0, 0}, +#undef V427 +#define V427 (*(V + 427)) + {0x13433, 0, 0, 0, 0}, +#undef V428 +#define V428 (*(V + 428)) + {0x13433, 26, 0, 0, 0}, +#undef V429 +#define V429 (*(V + 429)) + {0x13433, 0, -26, -26, 0}, +#undef V430 +#define V430 (*(V + 430)) + {0xB3493, 0, 0, 0, 0}, +#undef V431 +#define V431 (*(V + 431)) + {0x4BF093, 0, 0, 0, 0}, +#undef V432 +#define V432 (*(V + 432)) + {0x8BF093, 0, 0, 0, 0}, +#undef V433 +#define V433 (*(V + 433)) + {0xCBF093, 0, 0, 0, 0}, +#undef V434 +#define V434 (*(V + 434)) + {0x10BF093, 0, 0, 0, 0}, +#undef V435 +#define V435 (*(V + 435)) + {0x14BF093, 0, 0, 0, 0}, +#undef V436 +#define V436 (*(V + 436)) + {0x18BF093, 0, 0, 0, 0}, +#undef V437 +#define V437 (*(V + 437)) + {0x1CBF093, 0, 0, 0, 0}, +#undef V438 +#define V438 (*(V + 438)) + {0x20BF093, 0, 0, 0, 0}, +#undef V439 +#define V439 (*(V + 439)) + {0x24BF093, 0, 0, 0, 0}, +#undef V440 +#define V440 (*(V + 440)) + {0xBF093, 0, 0, 0, 0}, +#undef V441 +#define V441 (*(V + 441)) + {0xBF026, 0, 0, 0, 0}, +#undef V442 +#define V442 (*(V + 442)) + {0xB082F, 0, 0, 0, 0}, +#undef V443 +#define V443 (*(V + 443)) + {0x1F001, -10743, 0, 0, 0}, +#undef V444 +#define V444 (*(V + 444)) + {0x1F001, -3814, 0, 0, 0}, +#undef V445 +#define V445 (*(V + 445)) + {0x1F001, -10727, 0, 0, 0}, +#undef V446 +#define V446 (*(V + 446)) + {0x1F002, 0, -10795, -10795, 0}, +#undef V447 +#define V447 (*(V + 447)) + {0x1F002, 0, -10792, -10792, 0}, +#undef V448 +#define V448 (*(V + 448)) + {0x1F001, -10780, 0, 0, 0}, +#undef V449 +#define V449 (*(V + 449)) + {0x1F001, -10749, 0, 0, 0}, +#undef V450 +#define V450 (*(V + 450)) + {0x1F001, -10783, 0, 0, 0}, +#undef V451 +#define V451 (*(V + 451)) + {0x1F001, -10782, 0, 0, 0}, +#undef V452 +#define V452 (*(V + 452)) + {0x1F001, -10815, 0, 0, 0}, +#undef V453 +#define V453 (*(V + 453)) + {0x1F002, 0, -7264, -7264, 0}, +#undef V454 +#define V454 (*(V + 454)) + {0x1F007, 0, 0, 0, 0}, +#undef V455 +#define V455 (*(V + 455)) + {0x1F012, 0, 0, 0, 0}, +#undef V456 +#define V456 (*(V + 456)) + {0xF00D, 0, 0, 0, 218}, +#undef V457 +#define V457 (*(V + 457)) + {0x1F00F, 0, 0, 0, 224}, +#undef V458 +#define V458 (*(V + 458)) + {0x13412, 0, 0, 0, 0}, +#undef V459 +#define V459 (*(V + 459)) + {0x1F009, 0, 0, 0, 0}, +#undef V460 +#define V460 (*(V + 460)) + {0x1A809, 0, 0, 0, 0}, +#undef V461 +#define V461 (*(V + 461)) + {0x500D, 0, 0, 0, 8}, +#undef V462 +#define V462 (*(V + 462)) + {0x1A804, 0, 0, 0, 0}, +#undef V463 +#define V463 (*(V + 463)) + {0x13409, 0, 0, 0, 0}, +#undef V464 +#define V464 (*(V + 464)) + {0x1F008, 0, 0, 0, 0}, +#undef V465 +#define V465 (*(V + 465)) + {0x1A808, 0, 0, 0, 0}, +#undef V466 +#define V466 (*(V + 466)) + {0xBF02E, 0, 0, 0, 0}, +#undef V467 +#define V467 (*(V + 467)) + {0x13408, 0, 0, 0, 0}, +#undef V468 +#define V468 (*(V + 468)) + {0x13413, 0, 0, 0, 0}, +#undef V469 +#define V469 (*(V + 469)) + {0x1F001, -35332, 0, 0, 0}, +#undef V470 +#define V470 (*(V + 470)) + {0x1F032, 0, 0, 0, 0}, +#undef V471 +#define V471 (*(V + 471)) + {0x1F001, -42280, 0, 0, 0}, +#undef V472 +#define V472 (*(V + 472)) + {0x1F001, -42308, 0, 0, 0}, +#undef V473 +#define V473 (*(V + 473)) + {0x1F001, -42319, 0, 0, 0}, +#undef V474 +#define V474 (*(V + 474)) + {0x1F001, -42315, 0, 0, 0}, +#undef V475 +#define V475 (*(V + 475)) + {0x1F001, -42305, 0, 0, 0}, +#undef V476 +#define V476 (*(V + 476)) + {0x1F001, -42258, 0, 0, 0}, +#undef V477 +#define V477 (*(V + 477)) + {0x1F001, -42282, 0, 0, 0}, +#undef V478 +#define V478 (*(V + 478)) + {0x1F001, -42261, 0, 0, 0}, +#undef V479 +#define V479 (*(V + 479)) + {0x1F001, 928, 0, 0, 0}, +#undef V480 +#define V480 (*(V + 480)) + {0x1F002, 0, -928, -928, 0}, +#undef V481 +#define V481 (*(V + 481)) + {0x1F002, 0, -38864, -38864, 0}, +#undef V482 +#define V482 (*(V + 482)) + {0x1F020, 0, 0, 0, 0}, +#undef V483 +#define V483 (*(V + 483)) + {0x1F220, 0, 0, 0, 0}, +#undef V484 +#define V484 (*(V + 484)) + {0x1F01F, 0, 0, 0, 0}, +#undef V485 +#define V485 (*(V + 485)) + {0x1F200, 0, 0, 0, 0}, +#undef V486 +#define V486 (*(V + 486)) + {0x10807, 0, 0, 0, 0}, +#undef V487 +#define V487 (*(V + 487)) + {0x20806, 0, 0, 0, 0}, +#undef V488 +#define V488 (*(V + 488)) + {0xF00D, 0, 0, 0, 26}, +#undef V489 +#define V489 (*(V + 489)) + {0x23406, 0, 0, 0, 0}, +#undef V490 +#define V490 (*(V + 490)) + {0xF032, 0, 0, 0, 0}, +#undef V491 +#define V491 (*(V + 491)) + {0x3431, 0, 0, 0, 0}, +#undef V492 +#define V492 (*(V + 492)) + {0xB3421, 0, 0, 0, 0}, +#undef V493 +#define V493 (*(V + 493)) + {0xB3429, 0, 0, 0, 0}, +#undef V494 +#define V494 (*(V + 494)) + {0xB3424, 0, 0, 0, 0}, +#undef V495 +#define V495 (*(V + 495)) + {0xB3426, 0, 0, 0, 0}, +#undef V496 +#define V496 (*(V + 496)) + {0x7342C, 0, 0, 0, 0}, +#undef V497 +#define V497 (*(V + 497)) + {0x5342A, 0, 0, 0, 0}, +#undef V498 +#define V498 (*(V + 498)) + {0x43422, 0, 0, 0, 0}, +#undef V499 +#define V499 (*(V + 499)) + {0xF11E, 0, 0, 0, 0}, +#undef V500 +#define V500 (*(V + 500)) + {0xB3438, 0, 0, 0, 0}, +#undef V501 +#define V501 (*(V + 501)) + {0x7342A, 0, 0, 0, 0}, +#undef V502 +#define V502 (*(V + 502)) + {0x33490, 0, 0, 0, 0}, +#undef V503 +#define V503 (*(V + 503)) + {0x433490, 0, 0, 0, 0}, +#undef V504 +#define V504 (*(V + 504)) + {0x833490, 0, 0, 0, 0}, +#undef V505 +#define V505 (*(V + 505)) + {0xC33490, 0, 0, 0, 0}, +#undef V506 +#define V506 (*(V + 506)) + {0x1033490, 0, 0, 0, 0}, +#undef V507 +#define V507 (*(V + 507)) + {0x1433490, 0, 0, 0, 0}, +#undef V508 +#define V508 (*(V + 508)) + {0x1833490, 0, 0, 0, 0}, +#undef V509 +#define V509 (*(V + 509)) + {0x1C33490, 0, 0, 0, 0}, +#undef V510 +#define V510 (*(V + 510)) + {0x2033490, 0, 0, 0, 0}, +#undef V511 +#define V511 (*(V + 511)) + {0x2433490, 0, 0, 0, 0}, +#undef V512 +#define V512 (*(V + 512)) + {0x13401, 32, 0, 0, 0}, +#undef V513 +#define V513 (*(V + 513)) + {0x13402, 0, -32, -32, 0}, +#undef V514 +#define V514 (*(V + 514)) + {0xB342E, 0, 0, 0, 0}, +#undef V515 +#define V515 (*(V + 515)) + {0xBF01B, 0, 0, 0, 0}, +#undef V516 +#define V516 (*(V + 516)) + {0xBF011, 0, 0, 0, 0}, +#undef V517 +#define V517 (*(V + 517)) + {0x3F013, 0, 0, 0, 0}, +#undef V518 +#define V518 (*(V + 518)) + {0x1F001, 40, 0, 0, 0}, +#undef V519 +#define V519 (*(V + 519)) + {0x1F002, 0, -40, -40, 0}, +#undef V520 +#define V520 (*(V + 520)) + {0x2F013, 0, 0, 0, 0}, +#undef V521 +#define V521 (*(V + 521)) + {0x2F033, 0, 0, 0, 0}, +#undef V522 +#define V522 (*(V + 522)) + {0x42F093, 0, 0, 0, 0}, +#undef V523 +#define V523 (*(V + 523)) + {0x82F093, 0, 0, 0, 0}, +#undef V524 +#define V524 (*(V + 524)) + {0xC2F093, 0, 0, 0, 0}, +#undef V525 +#define V525 (*(V + 525)) + {0x102F093, 0, 0, 0, 0}, +#undef V526 +#define V526 (*(V + 526)) + {0x2F02C, 0, 0, 0, 0}, +#undef V527 +#define V527 (*(V + 527)) + {0x2F001, 64, 0, 0, 0}, +#undef V528 +#define V528 (*(V + 528)) + {0x2F002, 0, -64, -64, 0}, +#undef V529 +#define V529 (*(V + 529)) + {0x46F093, 0, 0, 0, 0}, +#undef V530 +#define V530 (*(V + 530)) + {0x86F093, 0, 0, 0, 0}, +#undef V531 +#define V531 (*(V + 531)) + {0xC6F093, 0, 0, 0, 0}, +#undef V532 +#define V532 (*(V + 532)) + {0x106F093, 0, 0, 0, 0}, +#undef V533 +#define V533 (*(V + 533)) + {0x146F093, 0, 0, 0, 0}, +#undef V534 +#define V534 (*(V + 534)) + {0x186F093, 0, 0, 0, 0}, +#undef V535 +#define V535 (*(V + 535)) + {0x1C6F093, 0, 0, 0, 0}, +#undef V536 +#define V536 (*(V + 536)) + {0x206F093, 0, 0, 0, 0}, +#undef V537 +#define V537 (*(V + 537)) + {0x246F093, 0, 0, 0, 0}, +#undef V538 +#define V538 (*(V + 538)) + {0x6F013, 0, 0, 0, 0}, +#undef V539 +#define V539 (*(V + 539)) + {0x1F01B, 0, 0, 0, 0}, +#undef V540 +#define V540 (*(V + 540)) + {0x1F00D, 0, 0, 0, 9}, +#undef V541 +#define V541 (*(V + 541)) + {0x10833, 0, 0, 0, 0}, +#undef V542 +#define V542 (*(V + 542)) + {0x1F00F, 0, 0, 0, 216}, +#undef V543 +#define V543 (*(V + 543)) + {0x1F00F, 0, 0, 0, 226}, +#undef V544 +#define V544 (*(V + 544)) + {0x1342F, 0, 0, 0, 0}, +#undef V545 +#define V545 (*(V + 545)) + {0x2F001, 34, 0, 0, 0}, +#undef V546 +#define V546 (*(V + 546)) + {0x2F002, 0, -34, -34, 0}, + }; + + static const NUnicode::NPrivate::TUnidataTable::TValuePtr P[][32] = { + { + &V0, &V0, &V0, &V0, &V0, &V0, &V0, &V0, + &V0, &V1, &V2, &V1, &V2, &V2, &V0, &V0, + &V0, &V0, &V0, &V0, &V0, &V0, &V0, &V0, + &V0, &V0, &V0, &V0, &V3, &V3, &V3, &V4, + }, // P[0] + { + &V5, &V6, &V7, &V8, &V9, &V8, &V10, &V11, + &V12, &V13, &V10, &V14, &V15, &V16, &V15, &V17, + &V18, &V19, &V20, &V21, &V22, &V23, &V24, &V25, + &V26, &V27, &V15, &V6, &V28, &V28, &V28, &V6, + }, // P[1] + { + &V10, &V29, &V29, &V29, &V29, &V29, &V29, &V30, + &V30, &V30, &V30, &V30, &V30, &V30, &V30, &V30, + &V30, &V30, &V30, &V30, &V30, &V30, &V30, &V30, + &V30, &V30, &V30, &V12, &V10, &V13, &V31, &V32, + }, // P[2] + { + &V31, &V33, &V33, &V33, &V33, &V33, &V33, &V34, + &V34, &V34, &V34, &V34, &V34, &V34, &V34, &V34, + &V34, &V34, &V34, &V34, &V34, &V34, &V34, &V34, + &V34, &V34, &V34, &V12, &V28, &V13, &V28, &V0, + }, // P[3] + { + &V0, &V0, &V0, &V0, &V0, &V35, &V0, &V0, + &V0, &V0, &V0, &V0, &V0, &V0, &V0, &V0, + &V0, &V0, &V0, &V0, &V0, &V0, &V0, &V0, + &V0, &V0, &V0, &V0, &V0, &V0, &V0, &V0, + }, // P[4] + { + &V36, &V10, &V9, &V9, &V9, &V9, &V37, &V10, + &V38, &V37, &V39, &V40, &V28, &V41, &V37, &V38, + &V42, &V43, &V44, &V45, &V38, &V46, &V10, &V47, + &V38, &V48, &V39, &V49, &V50, &V50, &V50, &V10, + }, // P[5] + { + &V51, &V51, &V51, &V51, &V51, &V51, &V30, &V51, + &V51, &V51, &V51, &V51, &V51, &V51, &V51, &V51, + &V30, &V51, &V51, &V51, &V51, &V51, &V51, &V28, + &V30, &V51, &V51, &V51, &V51, &V51, &V30, &V52, + }, // P[6] + { + &V53, &V53, &V53, &V53, &V53, &V53, &V34, &V53, + &V53, &V53, &V53, &V53, &V53, &V53, &V53, &V53, + &V34, &V53, &V53, &V53, &V53, &V53, &V53, &V28, + &V34, &V53, &V53, &V53, &V53, &V53, &V34, &V54, + }, // P[7] + { + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V57, &V58, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + }, // P[8] + { + &V55, &V56, &V55, &V56, &V55, &V56, &V57, &V58, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V59, &V60, &V61, &V62, &V55, &V56, &V55, &V56, + &V52, &V55, &V56, &V55, &V56, &V55, &V56, &V61, + }, // P[9] + { + &V62, &V57, &V58, &V55, &V56, &V55, &V56, &V55, + &V56, &V63, &V57, &V58, &V55, &V56, &V55, &V56, + &V55, &V56, &V57, &V58, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + }, // P[10] + { + &V55, &V56, &V55, &V56, &V55, &V56, &V57, &V58, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V64, &V55, &V56, &V55, &V56, &V55, &V56, &V65, + }, // P[11] + { + &V66, &V67, &V57, &V58, &V57, &V58, &V68, &V57, + &V58, &V69, &V69, &V57, &V58, &V52, &V70, &V71, + &V72, &V57, &V58, &V69, &V73, &V74, &V75, &V76, + &V57, &V58, &V77, &V52, &V75, &V78, &V79, &V80, + }, // P[12] + { + &V55, &V56, &V57, &V58, &V57, &V58, &V81, &V57, + &V58, &V81, &V52, &V52, &V57, &V58, &V81, &V55, + &V56, &V82, &V82, &V57, &V58, &V57, &V58, &V83, + &V57, &V58, &V52, &V84, &V57, &V58, &V52, &V85, + }, // P[13] + { + &V84, &V84, &V84, &V84, &V86, &V87, &V88, &V86, + &V87, &V88, &V86, &V87, &V88, &V55, &V56, &V55, + &V56, &V55, &V56, &V55, &V56, &V55, &V56, &V55, + &V56, &V55, &V56, &V55, &V56, &V89, &V55, &V56, + }, // P[14] + { + &V55, &V56, &V55, &V56, &V57, &V58, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V90, &V86, &V87, &V88, &V55, &V56, &V91, &V92, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + }, // P[15] + { + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V57, &V58, &V55, &V56, + }, // P[16] + { + &V93, &V52, &V57, &V58, &V57, &V58, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V52, &V52, &V52, &V52, + &V52, &V52, &V94, &V57, &V58, &V95, &V96, &V97, + }, // P[17] + { + &V97, &V57, &V58, &V98, &V99, &V100, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V101, &V102, &V103, &V104, &V105, &V52, &V106, &V106, + &V52, &V107, &V52, &V108, &V109, &V52, &V52, &V52, + }, // P[18] + { + &V106, &V110, &V52, &V111, &V52, &V112, &V113, &V52, + &V114, &V115, &V113, &V116, &V117, &V52, &V52, &V115, + &V52, &V118, &V119, &V52, &V52, &V120, &V52, &V52, + &V52, &V52, &V52, &V52, &V52, &V121, &V52, &V52, + }, // P[19] + { + &V122, &V52, &V52, &V122, &V52, &V52, &V52, &V123, + &V122, &V124, &V125, &V125, &V126, &V52, &V52, &V52, + &V52, &V52, &V127, &V52, &V84, &V52, &V52, &V52, + &V52, &V52, &V52, &V52, &V52, &V128, &V129, &V52, + }, // P[20] + { + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V130, &V130, &V130, &V130, &V130, &V130, &V130, &V130, + &V130, &V131, &V131, &V132, &V132, &V132, &V132, &V132, + }, // P[21] + { + &V132, &V132, &V31, &V31, &V31, &V31, &V131, &V133, + &V131, &V131, &V131, &V131, &V131, &V131, &V131, &V131, + &V134, &V134, &V31, &V31, &V31, &V31, &V31, &V31, + &V38, &V38, &V38, &V38, &V38, &V38, &V31, &V31, + }, // P[22] + { + &V130, &V130, &V130, &V130, &V130, &V31, &V31, &V31, + &V31, &V31, &V31, &V31, &V131, &V31, &V132, &V31, + &V31, &V31, &V31, &V31, &V31, &V31, &V31, &V31, + &V31, &V31, &V31, &V31, &V31, &V31, &V31, &V31, + }, // P[23] + { + &V135, &V135, &V135, &V135, &V135, &V136, &V135, &V135, + &V135, &V135, &V135, &V135, &V135, &V136, &V136, &V135, + &V136, &V135, &V136, &V135, &V135, &V137, &V138, &V138, + &V138, &V138, &V137, &V139, &V138, &V138, &V138, &V138, + }, // P[24] + { + &V138, &V140, &V140, &V141, &V141, &V141, &V141, &V142, + &V142, &V138, &V138, &V138, &V138, &V141, &V141, &V138, + &V141, &V141, &V138, &V138, &V143, &V143, &V143, &V143, + &V144, &V138, &V138, &V138, &V138, &V136, &V136, &V136, + }, // P[25] + { + &V145, &V145, &V135, &V145, &V145, &V146, &V136, &V138, + &V138, &V138, &V136, &V136, &V136, &V138, &V138, &V147, + &V136, &V136, &V136, &V138, &V138, &V138, &V138, &V136, + &V137, &V138, &V138, &V136, &V148, &V149, &V149, &V148, + }, // P[26] + { + &V149, &V149, &V148, &V136, &V136, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + &V57, &V58, &V57, &V58, &V150, &V31, &V57, &V58, + &V151, &V151, &V152, &V79, &V79, &V79, &V153, &V154, + }, // P[27] + { + &V151, &V151, &V151, &V151, &V38, &V155, &V156, &V153, + &V157, &V157, &V157, &V151, &V158, &V151, &V159, &V159, + &V90, &V30, &V30, &V30, &V30, &V30, &V30, &V30, + &V30, &V30, &V30, &V30, &V30, &V30, &V30, &V30, + }, // P[28] + { + &V30, &V30, &V151, &V30, &V30, &V30, &V30, &V30, + &V30, &V30, &V51, &V51, &V160, &V161, &V161, &V161, + &V90, &V34, &V34, &V34, &V34, &V34, &V34, &V34, + &V34, &V34, &V34, &V34, &V34, &V34, &V34, &V34, + }, // P[29] + { + &V34, &V34, &V162, &V34, &V34, &V34, &V34, &V34, + &V34, &V34, &V53, &V53, &V163, &V164, &V164, &V165, + &V166, &V167, &V168, &V169, &V169, &V170, &V171, &V172, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + }, // P[30] + { + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V173, &V174, &V175, &V176, &V177, &V178, &V28, &V57, + &V58, &V179, &V57, &V58, &V52, &V93, &V93, &V93, + }, // P[31] + { + &V180, &V180, &V181, &V180, &V181, &V181, &V181, &V180, + &V181, &V181, &V181, &V181, &V180, &V180, &V180, &V181, + &V30, &V30, &V30, &V30, &V30, &V30, &V30, &V30, + &V30, &V51, &V30, &V30, &V30, &V30, &V30, &V30, + }, // P[32] + { + &V30, &V30, &V30, &V30, &V30, &V30, &V30, &V30, + &V30, &V30, &V30, &V30, &V30, &V30, &V30, &V30, + &V34, &V34, &V34, &V34, &V34, &V34, &V34, &V34, + &V34, &V53, &V34, &V34, &V34, &V34, &V34, &V34, + }, // P[33] + { + &V34, &V34, &V34, &V34, &V34, &V34, &V34, &V34, + &V34, &V34, &V34, &V34, &V34, &V34, &V34, &V34, + &V182, &V182, &V183, &V182, &V183, &V183, &V183, &V182, + &V183, &V183, &V183, &V183, &V182, &V182, &V182, &V183, + }, // P[34] + { + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V55, &V56, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + }, // P[35] + { + &V57, &V58, &V184, &V136, &V136, &V136, &V136, &V136, + &V185, &V185, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + }, // P[36] + { + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + }, // P[37] + { + &V186, &V55, &V56, &V57, &V58, &V57, &V58, &V57, + &V58, &V57, &V58, &V57, &V58, &V57, &V58, &V187, + &V55, &V56, &V55, &V56, &V57, &V58, &V55, &V56, + &V57, &V58, &V55, &V56, &V55, &V56, &V55, &V56, + }, // P[38] + { + &V57, &V58, &V55, &V56, &V55, &V56, &V55, &V56, + &V57, &V58, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V57, &V58, + &V55, &V56, &V57, &V58, &V57, &V58, &V57, &V58, + }, // P[39] + { + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V151, &V188, &V188, &V188, &V188, &V188, &V188, &V188, + &V188, &V188, &V188, &V188, &V188, &V188, &V188, &V188, + }, // P[40] + { + &V188, &V188, &V188, &V188, &V188, &V188, &V188, &V188, + &V188, &V188, &V188, &V188, &V188, &V188, &V188, &V188, + &V188, &V188, &V188, &V188, &V188, &V188, &V188, &V151, + &V151, &V132, &V189, &V189, &V189, &V189, &V189, &V189, + }, // P[41] + { + &V151, &V190, &V190, &V190, &V190, &V190, &V190, &V190, + &V190, &V190, &V190, &V190, &V190, &V190, &V190, &V190, + &V190, &V190, &V190, &V190, &V190, &V190, &V190, &V190, + &V190, &V190, &V190, &V190, &V190, &V190, &V190, &V190, + }, // P[42] + { + &V190, &V190, &V190, &V190, &V190, &V190, &V190, &V63, + &V151, &V191, &V192, &V151, &V151, &V37, &V37, &V9, + &V151, &V138, &V136, &V136, &V136, &V136, &V138, &V136, + &V136, &V136, &V193, &V138, &V136, &V136, &V136, &V136, + }, // P[43] + { + &V136, &V136, &V138, &V138, &V138, &V138, &V138, &V138, + &V136, &V136, &V138, &V136, &V136, &V193, &V194, &V136, + &V195, &V196, &V197, &V198, &V199, &V200, &V201, &V202, + &V203, &V204, &V204, &V205, &V206, &V207, &V208, &V209, + }, // P[44] + { + &V210, &V211, &V212, &V210, &V136, &V138, &V210, &V203, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + }, // P[45] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V151, &V151, &V151, &V151, &V151, + &V213, &V213, &V213, &V210, &V210, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[46] + { + &V214, &V214, &V214, &V214, &V214, &V214, &V28, &V28, + &V215, &V8, &V8, &V216, &V15, &V217, &V37, &V37, + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + &V218, &V219, &V220, &V221, &V41, &V151, &V217, &V221, + }, // P[47] + { + &V222, &V222, &V223, &V223, &V223, &V223, &V223, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + }, // P[48] + { + &V224, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V225, &V226, &V227, &V218, &V219, + &V220, &V228, &V229, &V135, &V135, &V141, &V138, &V136, + &V136, &V136, &V136, &V136, &V138, &V136, &V136, &V138, + }, // P[49] + { + &V230, &V231, &V232, &V233, &V234, &V235, &V236, &V237, + &V238, &V239, &V8, &V240, &V240, &V217, &V222, &V222, + &V241, &V222, &V222, &V222, &V222, &V242, &V242, &V242, + &V242, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + }, // P[50] + { + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + }, // P[51] + { + &V223, &V222, &V223, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V223, &V221, &V222, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V214, &V37, &V136, + }, // P[52] + { + &V136, &V136, &V136, &V138, &V136, &V243, &V243, &V136, + &V136, &V37, &V138, &V136, &V136, &V138, &V222, &V222, + &V244, &V245, &V246, &V247, &V248, &V249, &V250, &V251, + &V252, &V253, &V222, &V222, &V222, &V254, &V254, &V222, + }, // P[53] + { + &V217, &V221, &V221, &V221, &V221, &V221, &V221, &V221, + &V221, &V221, &V217, &V217, &V217, &V217, &V151, &V41, + &V222, &V255, &V222, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + }, // P[54] + { + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + &V136, &V138, &V136, &V136, &V138, &V136, &V136, &V138, + &V138, &V138, &V136, &V138, &V138, &V136, &V138, &V136, + }, // P[55] + { + &V136, &V136, &V138, &V136, &V138, &V136, &V138, &V136, + &V138, &V136, &V136, &V151, &V151, &V222, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + }, // P[56] + { + &V222, &V222, &V222, &V222, &V222, &V222, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V222, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[57] + { + &V256, &V257, &V258, &V259, &V260, &V261, &V262, &V263, + &V264, &V265, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + }, // P[58] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V136, &V136, &V136, &V136, &V136, + &V136, &V136, &V138, &V136, &V266, &V266, &V37, &V10, + &V6, &V6, &V266, &V151, &V151, &V151, &V151, &V151, + }, // P[59] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V136, &V136, + &V136, &V136, &V267, &V136, &V136, &V136, &V136, &V136, + }, // P[60] + { + &V136, &V136, &V136, &V136, &V267, &V136, &V136, &V136, + &V267, &V136, &V136, &V136, &V136, &V136, &V151, &V151, + &V210, &V210, &V210, &V210, &V210, &V210, &V210, &V210, + &V210, &V210, &V210, &V210, &V210, &V210, &V210, &V151, + }, // P[61] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V138, &V138, &V138, &V151, &V151, &V210, &V151, + }, // P[62] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[63] + { + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V151, &V222, &V222, + &V222, &V222, &V222, &V222, &V222, &V222, &V151, &V151, + }, // P[64] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V136, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + }, // P[65] + { + &V136, &V136, &V214, &V138, &V136, &V136, &V138, &V136, + &V136, &V138, &V136, &V136, &V136, &V138, &V138, &V138, + &V225, &V226, &V227, &V136, &V136, &V136, &V138, &V136, + &V136, &V138, &V138, &V136, &V136, &V136, &V136, &V136, + }, // P[66] + { + &V147, &V147, &V147, &V268, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[67] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V269, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V269, &V84, &V84, &V269, &V84, &V84, &V84, + &V84, &V84, &V147, &V268, &V270, &V84, &V268, &V268, + }, // P[68] + { + &V268, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V268, &V268, &V268, &V268, &V271, &V268, &V268, + &V84, &V136, &V138, &V136, &V136, &V147, &V147, &V147, + &V272, &V272, &V272, &V272, &V272, &V272, &V272, &V272, + }, // P[69] + { + &V84, &V84, &V147, &V147, &V191, &V191, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + &V189, &V134, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[70] + { + &V84, &V147, &V268, &V268, &V151, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V151, &V151, &V84, + &V84, &V151, &V151, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[71] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V151, &V151, &V151, &V84, &V84, + &V84, &V84, &V151, &V151, &V283, &V84, &V284, &V268, + }, // P[72] + { + &V268, &V147, &V147, &V147, &V147, &V151, &V151, &V268, + &V268, &V151, &V151, &V285, &V285, &V271, &V84, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V284, + &V151, &V151, &V151, &V151, &V272, &V272, &V151, &V272, + }, // P[73] + { + &V84, &V84, &V147, &V147, &V151, &V151, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + &V84, &V84, &V9, &V9, &V286, &V286, &V286, &V286, + &V286, &V286, &V184, &V9, &V151, &V151, &V151, &V151, + }, // P[74] + { + &V151, &V147, &V147, &V268, &V151, &V84, &V84, &V84, + &V84, &V84, &V84, &V151, &V151, &V151, &V151, &V84, + &V84, &V151, &V151, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[75] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V272, &V151, &V84, &V272, &V151, + &V84, &V84, &V151, &V151, &V283, &V151, &V268, &V268, + }, // P[76] + { + &V268, &V147, &V147, &V151, &V151, &V151, &V151, &V147, + &V147, &V151, &V151, &V147, &V147, &V271, &V151, &V151, + &V151, &V147, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V272, &V272, &V272, &V84, &V151, &V272, &V151, + }, // P[77] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + &V147, &V147, &V84, &V84, &V84, &V147, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[78] + { + &V151, &V147, &V147, &V268, &V151, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V151, &V84, + &V84, &V84, &V151, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[79] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V151, &V84, &V84, &V84, + &V84, &V84, &V151, &V151, &V283, &V84, &V268, &V268, + }, // P[80] + { + &V268, &V147, &V147, &V147, &V147, &V147, &V151, &V147, + &V147, &V268, &V151, &V268, &V268, &V271, &V151, &V151, + &V84, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[81] + { + &V84, &V84, &V147, &V147, &V151, &V151, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + &V189, &V9, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V84, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[82] + { + &V151, &V147, &V268, &V268, &V151, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V151, &V151, &V84, + &V84, &V151, &V151, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[83] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V151, &V84, &V84, &V84, + &V84, &V84, &V151, &V151, &V283, &V84, &V284, &V147, + }, // P[84] + { + &V268, &V147, &V147, &V147, &V147, &V151, &V151, &V268, + &V285, &V151, &V151, &V285, &V285, &V271, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V287, &V284, + &V151, &V151, &V151, &V151, &V272, &V272, &V151, &V84, + }, // P[85] + { + &V84, &V84, &V147, &V147, &V151, &V151, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + &V184, &V84, &V286, &V286, &V286, &V286, &V286, &V286, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[86] + { + &V151, &V151, &V147, &V84, &V151, &V84, &V84, &V84, + &V84, &V84, &V84, &V151, &V151, &V151, &V84, &V84, + &V84, &V151, &V84, &V84, &V269, &V84, &V151, &V151, + &V151, &V84, &V84, &V151, &V84, &V151, &V84, &V84, + }, // P[87] + { + &V151, &V151, &V151, &V84, &V84, &V151, &V151, &V151, + &V84, &V84, &V84, &V151, &V151, &V151, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V151, &V151, &V151, &V151, &V284, &V268, + }, // P[88] + { + &V147, &V268, &V268, &V151, &V151, &V151, &V268, &V268, + &V268, &V151, &V285, &V285, &V285, &V271, &V151, &V151, + &V84, &V151, &V151, &V151, &V151, &V151, &V151, &V284, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[89] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + &V286, &V286, &V286, &V37, &V37, &V37, &V37, &V37, + &V37, &V9, &V37, &V151, &V151, &V151, &V151, &V151, + }, // P[90] + { + &V147, &V268, &V268, &V268, &V151, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V151, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[91] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V151, &V151, &V151, &V84, &V147, &V147, + }, // P[92] + { + &V147, &V268, &V268, &V268, &V268, &V151, &V147, &V147, + &V288, &V151, &V147, &V147, &V147, &V271, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V289, &V290, &V151, + &V84, &V84, &V84, &V151, &V151, &V151, &V151, &V151, + }, // P[93] + { + &V84, &V84, &V147, &V147, &V151, &V151, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V291, &V291, &V291, &V291, &V291, &V291, &V291, &V184, + }, // P[94] + { + &V84, &V147, &V268, &V268, &V151, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V151, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[95] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V151, &V84, &V84, &V84, + &V84, &V84, &V151, &V151, &V283, &V84, &V268, &V292, + }, // P[96] + { + &V285, &V268, &V284, &V268, &V268, &V151, &V292, &V285, + &V285, &V151, &V285, &V285, &V147, &V271, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V284, &V284, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V84, &V151, + }, // P[97] + { + &V84, &V84, &V147, &V147, &V151, &V151, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + &V151, &V84, &V84, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[98] + { + &V151, &V147, &V268, &V268, &V151, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V151, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[99] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V151, &V151, &V84, &V284, &V268, + }, // P[100] + { + &V268, &V147, &V147, &V147, &V147, &V151, &V268, &V268, + &V268, &V151, &V285, &V285, &V285, &V271, &V84, &V184, + &V151, &V151, &V151, &V151, &V84, &V84, &V84, &V284, + &V286, &V286, &V286, &V286, &V286, &V286, &V286, &V84, + }, // P[101] + { + &V84, &V84, &V147, &V147, &V151, &V151, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + &V286, &V286, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V184, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[102] + { + &V151, &V151, &V268, &V268, &V151, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V151, &V151, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[103] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V151, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V151, &V84, &V151, &V151, + }, // P[104] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V151, &V151, &V293, &V151, &V151, &V151, &V151, &V284, + &V268, &V268, &V147, &V147, &V147, &V151, &V147, &V151, + &V268, &V268, &V285, &V268, &V285, &V285, &V285, &V284, + }, // P[105] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + &V151, &V151, &V268, &V268, &V189, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[106] + { + &V151, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[107] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V147, &V84, &V39, &V147, &V147, &V147, &V147, + &V294, &V294, &V271, &V151, &V151, &V151, &V151, &V9, + }, // P[108] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V134, &V147, + &V295, &V295, &V295, &V295, &V147, &V147, &V147, &V189, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V189, &V189, &V151, &V151, &V151, &V151, + }, // P[109] + { + &V151, &V84, &V84, &V151, &V84, &V151, &V151, &V84, + &V84, &V151, &V84, &V151, &V151, &V84, &V151, &V151, + &V151, &V151, &V151, &V151, &V84, &V84, &V84, &V84, + &V151, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[110] + { + &V151, &V84, &V84, &V84, &V151, &V84, &V151, &V84, + &V151, &V151, &V84, &V84, &V151, &V84, &V84, &V84, + &V84, &V147, &V84, &V39, &V147, &V147, &V147, &V147, + &V296, &V296, &V151, &V147, &V147, &V84, &V151, &V151, + }, // P[111] + { + &V84, &V84, &V84, &V84, &V84, &V151, &V134, &V151, + &V297, &V297, &V297, &V297, &V147, &V147, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V39, &V39, &V84, &V84, + }, // P[112] + { + &V84, &V184, &V184, &V184, &V189, &V189, &V189, &V189, + &V189, &V189, &V189, &V189, &V298, &V189, &V189, &V189, + &V189, &V189, &V189, &V184, &V189, &V184, &V184, &V184, + &V138, &V138, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[113] + { + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V286, &V286, &V286, &V184, &V138, &V184, &V138, + &V184, &V299, &V12, &V13, &V12, &V13, &V268, &V268, + }, // P[114] + { + &V84, &V84, &V84, &V272, &V84, &V84, &V84, &V84, + &V151, &V84, &V84, &V84, &V84, &V272, &V84, &V84, + &V84, &V84, &V272, &V84, &V84, &V84, &V84, &V272, + &V84, &V84, &V84, &V84, &V272, &V84, &V84, &V84, + }, // P[115] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V272, &V84, &V84, &V84, &V151, &V151, &V151, + &V151, &V300, &V301, &V302, &V303, &V302, &V302, &V304, + &V302, &V304, &V301, &V301, &V301, &V301, &V147, &V268, + }, // P[116] + { + &V301, &V302, &V136, &V136, &V271, &V189, &V136, &V136, + &V84, &V84, &V84, &V84, &V84, &V147, &V147, &V147, + &V147, &V147, &V147, &V302, &V147, &V147, &V147, &V147, + &V151, &V147, &V147, &V147, &V147, &V302, &V147, &V147, + }, // P[117] + { + &V147, &V147, &V302, &V147, &V147, &V147, &V147, &V302, + &V147, &V147, &V147, &V147, &V302, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V302, &V147, &V147, &V147, &V151, &V184, &V184, + }, // P[118] + { + &V184, &V184, &V184, &V184, &V184, &V184, &V138, &V184, + &V184, &V184, &V184, &V184, &V184, &V151, &V184, &V184, + &V189, &V189, &V189, &V189, &V189, &V184, &V184, &V184, + &V184, &V189, &V189, &V151, &V151, &V151, &V151, &V151, + }, // P[119] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[120] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V269, &V84, + &V84, &V84, &V84, &V268, &V268, &V147, &V287, &V147, + &V147, &V268, &V147, &V147, &V147, &V147, &V147, &V283, + &V268, &V271, &V271, &V268, &V268, &V147, &V147, &V84, + }, // P[121] + { + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V189, &V189, &V189, &V189, &V189, &V189, + &V84, &V84, &V84, &V84, &V84, &V84, &V268, &V268, + &V147, &V147, &V84, &V84, &V84, &V84, &V147, &V147, + }, // P[122] + { + &V147, &V84, &V268, &V268, &V268, &V84, &V84, &V268, + &V268, &V268, &V268, &V268, &V268, &V268, &V84, &V84, + &V84, &V147, &V147, &V147, &V147, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[123] + { + &V84, &V84, &V147, &V268, &V268, &V147, &V147, &V268, + &V268, &V268, &V268, &V268, &V268, &V138, &V84, &V268, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V268, &V268, &V268, &V147, &V184, &V184, + }, // P[124] + { + &V305, &V305, &V305, &V305, &V305, &V305, &V305, &V305, + &V305, &V305, &V305, &V305, &V305, &V305, &V305, &V305, + &V305, &V305, &V305, &V305, &V305, &V305, &V305, &V305, + &V305, &V305, &V305, &V305, &V305, &V305, &V305, &V305, + }, // P[125] + { + &V305, &V305, &V305, &V305, &V305, &V305, &V151, &V305, + &V151, &V151, &V151, &V151, &V151, &V305, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[126] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V189, &V130, &V84, &V84, &V84, + }, // P[127] + { + &V306, &V306, &V306, &V306, &V306, &V306, &V306, &V306, + &V306, &V306, &V306, &V306, &V306, &V306, &V306, &V306, + &V306, &V306, &V306, &V306, &V306, &V306, &V306, &V306, + &V306, &V306, &V306, &V306, &V306, &V306, &V306, &V306, + }, // P[128] + { + &V307, &V308, &V308, &V308, &V308, &V308, &V308, &V308, + &V308, &V308, &V308, &V308, &V308, &V308, &V308, &V308, + &V308, &V308, &V308, &V308, &V308, &V308, &V307, &V307, + &V307, &V307, &V307, &V307, &V307, &V307, &V307, &V307, + }, // P[129] + { + &V307, &V307, &V307, &V307, &V307, &V307, &V307, &V307, + &V307, &V307, &V307, &V307, &V307, &V307, &V307, &V307, + &V307, &V307, &V307, &V307, &V307, &V307, &V307, &V307, + &V307, &V307, &V307, &V307, &V307, &V307, &V307, &V307, + }, // P[130] + { + &V307, &V307, &V307, &V307, &V307, &V307, &V307, &V307, + &V309, &V309, &V309, &V309, &V309, &V309, &V309, &V309, + &V309, &V309, &V309, &V309, &V309, &V309, &V309, &V309, + &V309, &V309, &V309, &V309, &V309, &V309, &V309, &V309, + }, // P[131] + { + &V309, &V309, &V309, &V310, &V310, &V310, &V310, &V310, + &V310, &V310, &V310, &V310, &V310, &V310, &V310, &V310, + &V310, &V310, &V310, &V310, &V310, &V310, &V310, &V310, + &V310, &V310, &V310, &V310, &V310, &V310, &V310, &V310, + }, // P[132] + { + &V310, &V310, &V310, &V310, &V310, &V310, &V310, &V310, + &V310, &V310, &V310, &V310, &V310, &V310, &V310, &V310, + &V310, &V310, &V310, &V310, &V310, &V310, &V310, &V310, + &V310, &V310, &V310, &V310, &V310, &V310, &V310, &V310, + }, // P[133] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V84, &V151, &V84, &V84, &V84, &V84, &V151, &V151, + }, // P[134] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[135] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + }, // P[136] + { + &V84, &V151, &V84, &V84, &V84, &V84, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[137] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[138] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V151, &V151, &V136, &V136, &V136, + }, // P[139] + { + &V189, &V189, &V191, &V191, &V191, &V191, &V191, &V191, + &V189, &V311, &V312, &V313, &V314, &V315, &V316, &V317, + &V318, &V319, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V286, &V286, &V286, &V286, &V151, &V151, &V151, + }, // P[140] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[141] + { + &V320, &V320, &V320, &V320, &V320, &V320, &V320, &V320, + &V320, &V320, &V320, &V320, &V320, &V320, &V320, &V320, + &V320, &V320, &V320, &V320, &V320, &V320, &V320, &V320, + &V320, &V320, &V320, &V320, &V320, &V320, &V320, &V320, + }, // P[142] + { + &V320, &V320, &V320, &V320, &V320, &V320, &V320, &V320, + &V320, &V320, &V320, &V320, &V320, &V320, &V320, &V320, + &V165, &V165, &V165, &V165, &V165, &V165, &V151, &V151, + &V172, &V172, &V172, &V172, &V172, &V172, &V151, &V151, + }, // P[143] + { + &V192, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[144] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V189, &V191, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[145] + { + &V5, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V12, &V13, &V151, &V151, &V151, + }, // P[146] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V189, &V189, &V189, &V321, &V321, + &V321, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[147] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V151, &V84, &V84, + &V84, &V84, &V147, &V147, &V271, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[148] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V147, &V147, &V271, &V189, &V189, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[149] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V147, &V147, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[150] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V151, &V84, &V84, + &V84, &V151, &V147, &V147, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[151] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V147, &V147, &V268, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V268, &V268, + }, // P[152] + { + &V268, &V268, &V268, &V268, &V268, &V268, &V147, &V268, + &V268, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V271, &V147, &V189, &V189, &V189, &V134, + &V189, &V189, &V189, &V9, &V84, &V136, &V151, &V151, + }, // P[153] + { + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V151, &V151, + &V291, &V291, &V291, &V291, &V291, &V291, &V291, &V291, + &V291, &V291, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[154] + { + &V10, &V6, &V6, &V6, &V6, &V10, &V192, &V10, + &V6, &V6, &V10, &V147, &V147, &V147, &V41, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[155] + { + &V84, &V84, &V84, &V132, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[156] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[157] + { + &V84, &V84, &V84, &V84, &V84, &V147, &V147, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[158] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V194, &V84, &V151, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[159] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[160] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + }, // P[161] + { + &V147, &V147, &V147, &V268, &V268, &V268, &V268, &V147, + &V147, &V268, &V268, &V268, &V151, &V151, &V151, &V151, + &V268, &V268, &V147, &V268, &V268, &V268, &V268, &V268, + &V268, &V193, &V136, &V138, &V151, &V151, &V151, &V151, + }, // P[162] + { + &V37, &V151, &V151, &V151, &V6, &V6, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[163] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[164] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[165] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V151, &V151, &V151, &V151, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V311, &V151, &V151, &V151, &V37, &V37, + }, // P[166] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[167] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V136, + &V138, &V268, &V268, &V147, &V151, &V151, &V189, &V189, + }, // P[168] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V268, &V147, &V268, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V151, + }, // P[169] + { + &V271, &V268, &V147, &V268, &V268, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V268, &V268, &V268, + &V268, &V268, &V268, &V147, &V147, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V151, &V151, &V138, + }, // P[170] + { + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[171] + { + &V189, &V189, &V189, &V189, &V189, &V189, &V189, &V134, + &V189, &V189, &V189, &V189, &V189, &V189, &V151, &V151, + &V136, &V136, &V136, &V136, &V136, &V138, &V138, &V138, + &V138, &V138, &V138, &V136, &V136, &V138, &V185, &V151, + }, // P[172] + { + &V147, &V147, &V147, &V147, &V268, &V84, &V269, &V84, + &V269, &V84, &V269, &V84, &V269, &V84, &V269, &V84, + &V84, &V84, &V269, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[173] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V283, &V284, &V147, &V147, + &V147, &V147, &V147, &V285, &V147, &V285, &V268, &V268, + }, // P[174] + { + &V285, &V285, &V147, &V285, &V322, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V151, &V151, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V189, &V189, &V189, &V189, &V189, &V189, + }, // P[175] + { + &V189, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V136, &V138, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V151, &V151, &V151, + }, // P[176] + { + &V147, &V147, &V268, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[177] + { + &V84, &V268, &V147, &V147, &V147, &V147, &V268, &V268, + &V147, &V147, &V322, &V271, &V147, &V147, &V84, &V84, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[178] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V283, &V268, + &V147, &V147, &V268, &V268, &V268, &V147, &V268, &V147, + &V147, &V147, &V322, &V322, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V189, &V189, &V189, &V189, + }, // P[179] + { + &V84, &V84, &V84, &V84, &V268, &V268, &V268, &V268, + &V268, &V268, &V268, &V268, &V147, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V268, &V268, &V147, &V283, + &V151, &V151, &V151, &V189, &V189, &V189, &V189, &V189, + }, // P[180] + { + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V84, &V84, &V84, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[181] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V134, &V134, &V134, &V134, &V134, &V134, &V189, &V189, + }, // P[182] + { + &V323, &V324, &V325, &V326, &V326, &V327, &V328, &V329, + &V330, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[183] + { + &V189, &V189, &V189, &V189, &V189, &V189, &V189, &V189, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V136, &V136, &V136, &V189, &V143, &V138, &V138, &V138, + &V138, &V138, &V136, &V136, &V138, &V138, &V138, &V138, + }, // P[184] + { + &V136, &V268, &V143, &V143, &V143, &V143, &V143, &V143, + &V143, &V84, &V84, &V84, &V84, &V138, &V84, &V84, + &V84, &V84, &V268, &V268, &V136, &V84, &V84, &V151, + &V136, &V136, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[185] + { + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + }, // P[186] + { + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V52, &V130, &V130, &V130, &V132, + &V130, &V130, &V130, &V130, &V130, &V130, &V130, &V130, + &V130, &V130, &V130, &V132, &V130, &V130, &V130, &V130, + }, // P[187] + { + &V130, &V130, &V130, &V130, &V130, &V130, &V130, &V130, + &V130, &V130, &V130, &V130, &V130, &V130, &V132, &V130, + &V130, &V130, &V130, &V130, &V130, &V130, &V130, &V130, + &V130, &V130, &V130, &V130, &V130, &V130, &V130, &V130, + }, // P[188] + { + &V130, &V130, &V130, &V130, &V130, &V130, &V130, &V130, + &V130, &V130, &V130, &V52, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V130, &V331, &V52, &V52, &V52, &V332, &V52, &V52, + }, // P[189] + { + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V130, &V130, &V130, &V130, &V130, + }, // P[190] + { + &V130, &V130, &V130, &V130, &V130, &V130, &V130, &V130, + &V130, &V130, &V130, &V130, &V130, &V130, &V130, &V130, + &V130, &V130, &V130, &V130, &V130, &V130, &V130, &V130, + &V130, &V130, &V130, &V130, &V130, &V130, &V130, &V130, + }, // P[191] + { + &V136, &V136, &V138, &V136, &V136, &V136, &V136, &V136, + &V136, &V136, &V138, &V136, &V136, &V149, &V333, &V138, + &V140, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + }, // P[192] + { + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V136, &V151, &V151, + &V151, &V151, &V151, &V136, &V148, &V138, &V136, &V138, + }, // P[193] + { + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + }, // P[194] + { + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V90, &V90, + &V90, &V90, &V63, &V334, &V52, &V52, &V335, &V52, + }, // P[195] + { + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V55, &V56, &V55, &V56, &V55, &V56, + &V55, &V56, &V57, &V58, &V57, &V58, &V57, &V58, + }, // P[196] + { + &V336, &V336, &V336, &V336, &V336, &V336, &V336, &V336, + &V337, &V337, &V337, &V337, &V337, &V337, &V337, &V337, + &V336, &V336, &V336, &V336, &V336, &V336, &V151, &V151, + &V337, &V337, &V337, &V337, &V337, &V337, &V151, &V151, + }, // P[197] + { + &V336, &V336, &V336, &V336, &V336, &V336, &V336, &V336, + &V337, &V337, &V337, &V337, &V337, &V337, &V337, &V337, + &V336, &V336, &V336, &V336, &V336, &V336, &V336, &V336, + &V337, &V337, &V337, &V337, &V337, &V337, &V337, &V337, + }, // P[198] + { + &V336, &V336, &V336, &V336, &V336, &V336, &V151, &V151, + &V337, &V337, &V337, &V337, &V337, &V337, &V151, &V151, + &V90, &V336, &V90, &V336, &V90, &V336, &V90, &V336, + &V151, &V337, &V151, &V337, &V151, &V337, &V151, &V337, + }, // P[199] + { + &V336, &V336, &V336, &V336, &V336, &V336, &V336, &V336, + &V337, &V337, &V337, &V337, &V337, &V337, &V337, &V337, + &V338, &V339, &V340, &V341, &V340, &V341, &V342, &V343, + &V344, &V345, &V346, &V347, &V348, &V349, &V151, &V151, + }, // P[200] + { + &V336, &V336, &V336, &V336, &V336, &V336, &V336, &V336, + &V350, &V350, &V350, &V350, &V350, &V350, &V350, &V350, + &V336, &V336, &V336, &V336, &V336, &V336, &V336, &V336, + &V350, &V350, &V350, &V350, &V350, &V350, &V350, &V350, + }, // P[201] + { + &V336, &V336, &V336, &V336, &V336, &V336, &V336, &V336, + &V350, &V350, &V350, &V350, &V350, &V350, &V350, &V350, + &V336, &V336, &V90, &V351, &V90, &V151, &V90, &V90, + &V337, &V337, &V352, &V353, &V354, &V38, &V355, &V38, + }, // P[202] + { + &V38, &V155, &V90, &V351, &V90, &V151, &V90, &V90, + &V356, &V357, &V356, &V357, &V354, &V155, &V155, &V155, + &V336, &V336, &V90, &V358, &V151, &V151, &V90, &V90, + &V337, &V337, &V359, &V360, &V151, &V155, &V155, &V155, + }, // P[203] + { + &V336, &V336, &V90, &V358, &V90, &V361, &V90, &V90, + &V337, &V337, &V362, &V363, &V364, &V155, &V365, &V365, + &V151, &V151, &V90, &V351, &V90, &V151, &V90, &V90, + &V366, &V367, &V368, &V369, &V354, &V365, &V38, &V151, + }, // P[204] + { + &V370, &V370, &V371, &V371, &V371, &V371, &V371, &V371, + &V371, &V371, &V371, &V372, &V373, &V373, &V374, &V375, + &V192, &V376, &V377, &V377, &V377, &V377, &V10, &V378, + &V379, &V380, &V381, &V379, &V40, &V49, &V382, &V40, + }, // P[205] + { + &V10, &V10, &V10, &V10, &V378, &V378, &V383, &V10, + &V384, &V385, &V386, &V386, &V386, &V386, &V386, &V36, + &V8, &V8, &V387, &V388, &V388, &V11, &V389, &V389, + &V10, &V40, &V49, &V10, &V383, &V6, &V378, &V32, + }, // P[206] + { + &V32, &V10, &V10, &V10, &V390, &V12, &V13, &V383, + &V383, &V383, &V10, &V10, &V10, &V10, &V10, &V6, + &V10, &V10, &V28, &V10, &V32, &V10, &V10, &V389, + &V10, &V10, &V10, &V10, &V10, &V10, &V10, &V371, + }, // P[207] + { + &V373, &V41, &V41, &V41, &V41, &V151, &V386, &V386, + &V41, &V386, &V41, &V41, &V41, &V41, &V41, &V41, + &V391, &V130, &V151, &V151, &V392, &V393, &V394, &V395, + &V396, &V397, &V398, &V399, &V400, &V401, &V402, &V130, + }, // P[208] + { + &V391, &V48, &V44, &V45, &V392, &V393, &V394, &V395, + &V396, &V397, &V398, &V399, &V400, &V401, &V402, &V151, + &V130, &V130, &V130, &V130, &V130, &V130, &V130, &V130, + &V130, &V130, &V130, &V130, &V130, &V151, &V151, &V151, + }, // P[209] + { + &V9, &V9, &V9, &V9, &V9, &V9, &V9, &V9, + &V403, &V9, &V9, &V9, &V9, &V9, &V9, &V9, + &V9, &V9, &V9, &V9, &V9, &V9, &V9, &V9, + &V9, &V9, &V9, &V9, &V9, &V9, &V9, &V151, + }, // P[210] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V136, &V136, &V143, &V143, &V136, &V136, &V136, &V136, + &V143, &V143, &V143, &V136, &V136, &V185, &V185, &V185, + }, // P[211] + { + &V185, &V136, &V185, &V185, &V185, &V143, &V143, &V136, + &V138, &V136, &V143, &V143, &V138, &V138, &V138, &V138, + &V136, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[212] + { + &V404, &V404, &V168, &V404, &V37, &V404, &V404, &V168, + &V37, &V404, &V63, &V168, &V168, &V168, &V63, &V63, + &V168, &V168, &V168, &V63, &V37, &V168, &V404, &V37, + &V28, &V168, &V168, &V168, &V168, &V168, &V37, &V37, + }, // P[213] + { + &V404, &V404, &V404, &V37, &V168, &V37, &V405, &V37, + &V168, &V37, &V406, &V407, &V168, &V168, &V42, &V63, + &V168, &V168, &V408, &V168, &V63, &V39, &V39, &V39, + &V39, &V63, &V37, &V404, &V63, &V63, &V168, &V168, + }, // P[214] + { + &V400, &V28, &V28, &V28, &V28, &V168, &V63, &V63, + &V63, &V63, &V37, &V28, &V37, &V37, &V409, &V184, + &V50, &V50, &V50, &V50, &V50, &V50, &V50, &V50, + &V50, &V50, &V50, &V50, &V50, &V50, &V50, &V50, + }, // P[215] + { + &V410, &V410, &V410, &V410, &V410, &V410, &V410, &V410, + &V410, &V410, &V410, &V410, &V410, &V410, &V410, &V410, + &V411, &V411, &V411, &V411, &V411, &V411, &V411, &V411, + &V411, &V411, &V411, &V411, &V411, &V411, &V411, &V411, + }, // P[216] + { + &V321, &V321, &V321, &V57, &V58, &V321, &V321, &V321, + &V321, &V50, &V37, &V37, &V151, &V151, &V151, &V151, + &V28, &V28, &V28, &V28, &V28, &V37, &V37, &V37, + &V37, &V37, &V412, &V412, &V37, &V37, &V37, &V37, + }, // P[217] + { + &V28, &V37, &V37, &V28, &V37, &V37, &V28, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V412, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[218] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V413, &V412, &V412, + &V37, &V37, &V28, &V37, &V28, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[219] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[220] + { + &V28, &V28, &V28, &V28, &V412, &V28, &V28, &V28, + &V28, &V412, &V28, &V28, &V412, &V28, &V28, &V28, + &V28, &V28, &V414, &V43, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[221] + { + &V28, &V28, &V28, &V28, &V412, &V28, &V412, &V28, + &V28, &V28, &V28, &V28, &V400, &V400, &V28, &V400, + &V400, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[222] + { + &V28, &V412, &V28, &V28, &V412, &V28, &V28, &V412, + &V28, &V412, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[223] + { + &V412, &V28, &V412, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V412, &V412, &V412, + &V412, &V412, &V28, &V28, &V412, &V412, &V28, &V28, + &V412, &V412, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[224] + { + &V412, &V412, &V28, &V28, &V412, &V412, &V28, &V28, + &V412, &V412, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[225] + { + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V412, &V412, &V412, &V412, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[226] + { + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[227] + { + &V412, &V412, &V412, &V412, &V28, &V28, &V28, &V28, + &V28, &V28, &V412, &V412, &V412, &V412, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[228] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V12, &V13, &V12, &V13, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[229] + { + &V28, &V28, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V415, &V416, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[230] + { + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[231] + { + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V37, &V28, &V37, &V37, &V37, + }, // P[232] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V184, &V37, &V37, + &V37, &V37, &V37, &V28, &V28, &V28, &V28, &V28, + }, // P[233] + { + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[234] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V28, &V28, &V28, &V28, + }, // P[235] + { + &V28, &V28, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V151, + }, // P[236] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[237] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[238] + { + &V417, &V418, &V419, &V420, &V421, &V422, &V423, &V424, + &V425, &V50, &V50, &V50, &V50, &V50, &V50, &V50, + &V50, &V50, &V50, &V50, &V417, &V418, &V419, &V420, + &V421, &V422, &V423, &V424, &V425, &V50, &V50, &V50, + }, // P[239] + { + &V50, &V50, &V50, &V50, &V50, &V50, &V50, &V50, + &V48, &V44, &V45, &V392, &V393, &V394, &V395, &V396, + &V397, &V426, &V426, &V426, &V426, &V426, &V426, &V426, + &V426, &V426, &V426, &V426, &V427, &V427, &V427, &V427, + }, // P[240] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V428, &V428, + &V428, &V428, &V428, &V428, &V428, &V428, &V428, &V428, + }, // P[241] + { + &V428, &V428, &V428, &V428, &V428, &V428, &V428, &V428, + &V428, &V428, &V428, &V428, &V428, &V428, &V428, &V428, + &V429, &V429, &V429, &V429, &V429, &V429, &V429, &V429, + &V429, &V429, &V429, &V429, &V429, &V429, &V429, &V429, + }, // P[242] + { + &V429, &V429, &V429, &V429, &V429, &V429, &V429, &V429, + &V429, &V429, &V430, &V291, &V291, &V291, &V291, &V291, + &V291, &V291, &V291, &V291, &V291, &V431, &V432, &V433, + &V434, &V435, &V436, &V437, &V438, &V439, &V291, &V440, + }, // P[243] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V28, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[244] + { + &V37, &V28, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[245] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[246] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V28, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[247] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V184, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[248] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V12, &V13, &V12, &V13, &V12, &V13, &V382, &V441, + &V12, &V13, &V12, &V13, &V12, &V13, &V431, &V432, + &V433, &V434, &V435, &V436, &V437, &V438, &V439, &V291, + }, // P[249] + { + &V431, &V432, &V433, &V434, &V435, &V436, &V437, &V438, + &V439, &V291, &V431, &V432, &V433, &V434, &V435, &V436, + &V437, &V438, &V439, &V291, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[250] + { + &V28, &V28, &V28, &V28, &V28, &V12, &V13, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[251] + { + &V28, &V28, &V28, &V28, &V28, &V28, &V12, &V13, + &V12, &V13, &V12, &V13, &V12, &V13, &V12, &V13, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[252] + { + &V28, &V28, &V28, &V12, &V13, &V12, &V13, &V12, + &V13, &V12, &V13, &V12, &V13, &V12, &V13, &V12, + &V13, &V12, &V13, &V12, &V13, &V12, &V13, &V12, + &V13, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[253] + { + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V12, &V13, &V12, &V13, &V28, &V28, &V28, &V28, + }, // P[254] + { + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V12, &V13, &V28, &V28, + }, // P[255] + { + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V400, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[256] + { + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V400, &V400, &V400, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[257] + { + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V442, &V28, &V28, &V28, + }, // P[258] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + &V28, &V28, &V28, &V28, &V28, &V28, &V28, &V28, + }, // P[259] + { + &V28, &V28, &V28, &V28, &V28, &V37, &V37, &V28, + &V28, &V28, &V28, &V28, &V28, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[260] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V151, &V151, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[261] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V151, &V151, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[262] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V151, &V151, &V151, &V37, &V37, &V37, + }, // P[263] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V151, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[264] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V37, &V37, &V37, &V37, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[265] + { + &V188, &V188, &V188, &V188, &V188, &V188, &V188, &V188, + &V188, &V188, &V188, &V188, &V188, &V188, &V188, &V188, + &V188, &V188, &V188, &V188, &V188, &V188, &V188, &V188, + &V188, &V188, &V188, &V188, &V188, &V188, &V188, &V188, + }, // P[266] + { + &V188, &V188, &V188, &V188, &V188, &V188, &V188, &V188, + &V188, &V188, &V188, &V188, &V188, &V188, &V188, &V151, + &V190, &V190, &V190, &V190, &V190, &V190, &V190, &V190, + &V190, &V190, &V190, &V190, &V190, &V190, &V190, &V190, + }, // P[267] + { + &V190, &V190, &V190, &V190, &V190, &V190, &V190, &V190, + &V190, &V190, &V190, &V190, &V190, &V190, &V190, &V190, + &V190, &V190, &V190, &V190, &V190, &V190, &V190, &V190, + &V190, &V190, &V190, &V190, &V190, &V190, &V190, &V151, + }, // P[268] + { + &V57, &V58, &V443, &V444, &V445, &V446, &V447, &V57, + &V58, &V57, &V58, &V57, &V58, &V448, &V449, &V450, + &V451, &V52, &V57, &V58, &V52, &V57, &V58, &V52, + &V52, &V52, &V52, &V52, &V130, &V130, &V452, &V452, + }, // P[269] + { + &V57, &V58, &V57, &V58, &V52, &V37, &V37, &V37, + &V37, &V37, &V37, &V57, &V58, &V57, &V58, &V136, + &V136, &V136, &V57, &V58, &V151, &V151, &V151, &V151, + &V151, &V6, &V6, &V6, &V10, &V291, &V6, &V10, + }, // P[270] + { + &V453, &V453, &V453, &V453, &V453, &V453, &V453, &V453, + &V453, &V453, &V453, &V453, &V453, &V453, &V453, &V453, + &V453, &V453, &V453, &V453, &V453, &V453, &V453, &V453, + &V453, &V453, &V453, &V453, &V453, &V453, &V453, &V453, + }, // P[271] + { + &V453, &V453, &V453, &V453, &V453, &V453, &V151, &V453, + &V151, &V151, &V151, &V151, &V151, &V453, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[272] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V130, + &V189, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V271, + }, // P[273] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[274] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + }, // P[275] + { + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + }, // P[276] + { + &V10, &V10, &V40, &V49, &V40, &V49, &V10, &V10, + &V10, &V40, &V49, &V10, &V40, &V49, &V10, &V10, + &V10, &V10, &V10, &V10, &V10, &V10, &V10, &V192, + &V10, &V10, &V192, &V10, &V40, &V49, &V10, &V10, + }, // P[277] + { + &V40, &V49, &V12, &V13, &V12, &V13, &V12, &V13, + &V12, &V13, &V10, &V10, &V10, &V10, &V6, &V133, + &V10, &V47, &V6, &V10, &V6, &V6, &V10, &V10, + &V10, &V10, &V377, &V377, &V6, &V10, &V10, &V10, + }, // P[278] + { + &V192, &V6, &V382, &V10, &V10, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[279] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V151, &V37, &V37, &V37, &V37, &V404, + }, // P[280] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V404, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[281] + { + &V404, &V404, &V404, &V404, &V404, &V404, &V404, &V404, + &V404, &V404, &V404, &V404, &V404, &V404, &V404, &V404, + &V404, &V404, &V404, &V404, &V404, &V404, &V404, &V404, + &V404, &V404, &V404, &V404, &V404, &V404, &V404, &V404, + }, // P[282] + { + &V404, &V404, &V404, &V404, &V404, &V404, &V404, &V404, + &V404, &V404, &V404, &V404, &V404, &V404, &V404, &V404, + &V404, &V404, &V404, &V404, &V404, &V404, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[283] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V151, &V151, &V151, &V151, + }, // P[284] + { + &V371, &V6, &V6, &V10, &V37, &V134, &V454, &V455, + &V12, &V13, &V12, &V13, &V382, &V441, &V382, &V441, + &V12, &V13, &V37, &V37, &V12, &V13, &V12, &V13, + &V12, &V13, &V12, &V13, &V377, &V382, &V441, &V441, + }, // P[285] + { + &V37, &V455, &V455, &V455, &V455, &V455, &V455, &V455, + &V455, &V455, &V456, &V194, &V137, &V193, &V457, &V457, + &V377, &V134, &V134, &V134, &V134, &V134, &V404, &V37, + &V458, &V458, &V458, &V134, &V84, &V10, &V37, &V37, + }, // P[286] + { + &V151, &V459, &V459, &V459, &V459, &V459, &V459, &V459, + &V459, &V459, &V459, &V459, &V460, &V459, &V460, &V459, + &V460, &V459, &V460, &V459, &V460, &V459, &V460, &V459, + &V460, &V459, &V460, &V459, &V460, &V459, &V460, &V459, + }, // P[287] + { + &V460, &V459, &V460, &V459, &V459, &V460, &V459, &V460, + &V459, &V460, &V459, &V459, &V459, &V459, &V459, &V459, + &V460, &V460, &V459, &V460, &V460, &V459, &V460, &V460, + &V459, &V460, &V460, &V459, &V460, &V460, &V459, &V459, + }, // P[288] + { + &V459, &V459, &V459, &V459, &V459, &V459, &V459, &V459, + &V459, &V459, &V459, &V459, &V459, &V459, &V459, &V459, + &V459, &V459, &V459, &V459, &V460, &V459, &V459, &V151, + &V151, &V461, &V461, &V38, &V38, &V134, &V462, &V463, + }, // P[289] + { + &V192, &V464, &V464, &V464, &V464, &V464, &V464, &V464, + &V464, &V464, &V464, &V464, &V465, &V464, &V465, &V464, + &V465, &V464, &V465, &V464, &V465, &V464, &V465, &V464, + &V465, &V464, &V465, &V464, &V465, &V464, &V465, &V464, + }, // P[290] + { + &V465, &V464, &V465, &V464, &V464, &V465, &V464, &V465, + &V464, &V465, &V464, &V464, &V464, &V464, &V464, &V464, + &V465, &V465, &V464, &V465, &V465, &V464, &V465, &V465, + &V464, &V465, &V465, &V464, &V465, &V465, &V464, &V464, + }, // P[291] + { + &V464, &V464, &V464, &V464, &V464, &V464, &V464, &V464, + &V464, &V464, &V464, &V464, &V464, &V464, &V464, &V464, + &V464, &V464, &V464, &V464, &V465, &V464, &V464, &V465, + &V465, &V465, &V465, &V466, &V134, &V134, &V462, &V467, + }, // P[292] + { + &V151, &V151, &V151, &V151, &V151, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[293] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V151, &V151, + &V151, &V39, &V39, &V39, &V39, &V39, &V39, &V39, + &V39, &V39, &V39, &V39, &V39, &V39, &V39, &V39, + }, // P[294] + { + &V39, &V39, &V39, &V39, &V39, &V39, &V39, &V39, + &V39, &V39, &V39, &V39, &V39, &V39, &V39, &V39, + &V39, &V39, &V39, &V39, &V39, &V39, &V39, &V39, + &V39, &V39, &V39, &V39, &V39, &V39, &V39, &V39, + }, // P[295] + { + &V39, &V39, &V39, &V39, &V39, &V39, &V39, &V39, + &V39, &V39, &V39, &V39, &V39, &V39, &V39, &V151, + &V184, &V184, &V468, &V468, &V468, &V468, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + }, // P[296] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V151, &V151, &V151, &V151, &V151, + }, // P[297] + { + &V37, &V37, &V37, &V37, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V464, &V464, &V464, &V464, &V464, &V464, &V464, &V464, + &V464, &V464, &V464, &V464, &V464, &V464, &V464, &V464, + }, // P[298] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V404, &V404, &V151, + }, // P[299] + { + &V468, &V468, &V468, &V468, &V468, &V468, &V468, &V468, + &V468, &V468, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + }, // P[300] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V286, &V286, &V286, &V286, &V286, &V286, &V286, &V286, + &V404, &V50, &V50, &V50, &V50, &V50, &V50, &V50, + &V50, &V50, &V50, &V50, &V50, &V50, &V50, &V50, + }, // P[301] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V404, &V404, &V404, &V184, + }, // P[302] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V50, &V50, &V50, &V50, &V50, &V50, &V50, + &V50, &V50, &V50, &V50, &V50, &V50, &V50, &V50, + }, // P[303] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V404, &V404, &V404, &V404, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + }, // P[304] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V151, + }, // P[305] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + }, // P[306] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V404, + &V404, &V404, &V404, &V427, &V427, &V427, &V427, &V427, + }, // P[307] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V404, &V404, + }, // P[308] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V404, + }, // P[309] + { + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + }, // P[310] + { + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V454, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[311] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V134, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[312] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V151, &V151, &V151, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[313] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[314] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V132, &V132, &V132, &V132, &V132, &V132, &V191, &V191, + }, // P[315] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V134, &V6, &V6, &V6, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[316] + { + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V84, &V84, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[317] + { + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V84, &V136, + &V185, &V185, &V185, &V10, &V136, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V136, &V10, &V133, + }, // P[318] + { + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V130, &V130, &V136, &V136, + }, // P[319] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V321, &V321, + &V321, &V321, &V321, &V321, &V321, &V321, &V321, &V321, + &V136, &V136, &V189, &V191, &V191, &V191, &V191, &V191, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[320] + { + &V31, &V31, &V31, &V31, &V31, &V31, &V31, &V31, + &V31, &V31, &V31, &V31, &V31, &V31, &V31, &V31, + &V31, &V31, &V31, &V31, &V31, &V31, &V31, &V131, + &V131, &V131, &V131, &V131, &V131, &V131, &V131, &V131, + }, // P[321] + { + &V31, &V31, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V52, &V52, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + }, // P[322] + { + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V130, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V52, &V57, &V58, &V57, &V58, &V469, &V57, &V58, + }, // P[323] + { + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V131, &V470, &V470, &V57, &V58, &V471, &V52, &V84, + &V57, &V58, &V57, &V58, &V52, &V52, &V57, &V58, + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + }, // P[324] + { + &V57, &V58, &V57, &V58, &V57, &V58, &V57, &V58, + &V57, &V58, &V472, &V473, &V474, &V475, &V472, &V151, + &V476, &V477, &V478, &V479, &V57, &V58, &V57, &V58, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[325] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V84, + &V130, &V130, &V52, &V84, &V84, &V84, &V84, &V84, + }, // P[326] + { + &V84, &V84, &V147, &V84, &V84, &V84, &V271, &V84, + &V84, &V84, &V84, &V147, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[327] + { + &V84, &V84, &V84, &V268, &V268, &V147, &V147, &V268, + &V37, &V37, &V37, &V37, &V151, &V151, &V151, &V151, + &V286, &V286, &V286, &V286, &V286, &V286, &V184, &V184, + &V9, &V42, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[328] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V10, &V10, &V10, &V10, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[329] + { + &V268, &V268, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[330] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V268, &V268, &V268, &V268, + &V268, &V268, &V268, &V268, &V268, &V268, &V268, &V268, + }, // P[331] + { + &V268, &V268, &V268, &V268, &V271, &V147, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V191, &V191, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[332] + { + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + &V136, &V136, &V84, &V84, &V84, &V84, &V84, &V84, + &V189, &V189, &V189, &V84, &V189, &V84, &V151, &V151, + }, // P[333] + { + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[334] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V147, &V147, + &V147, &V147, &V147, &V138, &V138, &V138, &V189, &V189, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[335] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V268, &V322, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V189, + }, // P[336] + { + &V306, &V306, &V306, &V306, &V306, &V306, &V306, &V306, + &V306, &V306, &V306, &V306, &V306, &V306, &V306, &V306, + &V306, &V306, &V306, &V306, &V306, &V306, &V306, &V306, + &V306, &V306, &V306, &V306, &V306, &V151, &V151, &V151, + }, // P[337] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V283, &V268, &V268, &V147, &V147, + &V147, &V147, &V268, &V268, &V147, &V268, &V268, &V268, + }, // P[338] + { + &V322, &V189, &V189, &V189, &V189, &V189, &V189, &V189, + &V189, &V189, &V189, &V189, &V189, &V189, &V151, &V134, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V189, &V189, + }, // P[339] + { + &V84, &V84, &V84, &V84, &V84, &V147, &V132, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V84, &V84, &V84, &V84, &V84, &V151, + }, // P[340] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V147, &V147, &V147, &V147, &V147, &V147, &V268, + &V268, &V147, &V147, &V268, &V268, &V147, &V147, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[341] + { + &V84, &V84, &V84, &V147, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V147, &V268, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V189, &V191, &V191, &V191, + }, // P[342] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V132, &V84, &V84, &V84, &V84, &V84, &V84, &V184, + &V184, &V184, &V84, &V268, &V147, &V268, &V84, &V84, + }, // P[343] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V136, &V84, &V136, &V136, &V138, &V84, &V84, &V136, + &V136, &V84, &V84, &V84, &V84, &V84, &V136, &V136, + }, // P[344] + { + &V84, &V136, &V84, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V84, &V84, &V134, &V189, &V189, + }, // P[345] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V268, &V147, &V147, &V268, &V268, + &V189, &V189, &V84, &V134, &V134, &V268, &V271, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[346] + { + &V151, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V151, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V151, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[347] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + }, // P[348] + { + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V52, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V480, &V52, &V52, &V52, &V52, + &V52, &V52, &V52, &V470, &V130, &V130, &V130, &V130, + }, // P[349] + { + &V52, &V52, &V52, &V52, &V52, &V52, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V481, &V481, &V481, &V481, &V481, &V481, &V481, &V481, + &V481, &V481, &V481, &V481, &V481, &V481, &V481, &V481, + }, // P[350] + { + &V481, &V481, &V481, &V481, &V481, &V481, &V481, &V481, + &V481, &V481, &V481, &V481, &V481, &V481, &V481, &V481, + &V481, &V481, &V481, &V481, &V481, &V481, &V481, &V481, + &V481, &V481, &V481, &V481, &V481, &V481, &V481, &V481, + }, // P[351] + { + &V84, &V84, &V84, &V268, &V268, &V147, &V268, &V268, + &V147, &V268, &V268, &V189, &V268, &V271, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[352] + { + &V269, &V269, &V269, &V269, &V269, &V269, &V269, &V269, + &V269, &V269, &V269, &V269, &V269, &V269, &V269, &V269, + &V269, &V269, &V269, &V269, &V269, &V269, &V269, &V269, + &V269, &V269, &V269, &V269, &V269, &V269, &V269, &V269, + }, // P[353] + { + &V269, &V269, &V269, &V269, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V307, &V307, &V307, &V307, &V307, &V307, &V307, &V307, + &V307, &V307, &V307, &V307, &V307, &V307, &V307, &V307, + }, // P[354] + { + &V307, &V307, &V307, &V307, &V307, &V307, &V307, &V151, + &V151, &V151, &V151, &V310, &V310, &V310, &V310, &V310, + &V310, &V310, &V310, &V310, &V310, &V310, &V310, &V310, + &V310, &V310, &V310, &V310, &V310, &V310, &V310, &V310, + }, // P[355] + { + &V310, &V310, &V310, &V310, &V310, &V310, &V310, &V310, + &V310, &V310, &V310, &V310, &V310, &V310, &V310, &V310, + &V310, &V310, &V310, &V310, &V310, &V310, &V310, &V310, + &V310, &V310, &V310, &V310, &V151, &V151, &V151, &V151, + }, // P[356] + { + &V482, &V482, &V482, &V482, &V482, &V482, &V482, &V482, + &V482, &V482, &V482, &V482, &V482, &V482, &V482, &V482, + &V482, &V482, &V482, &V482, &V482, &V482, &V482, &V482, + &V482, &V482, &V482, &V482, &V482, &V482, &V482, &V482, + }, // P[357] + { + &V483, &V483, &V483, &V483, &V483, &V483, &V483, &V483, + &V483, &V483, &V483, &V483, &V483, &V483, &V483, &V483, + &V483, &V483, &V483, &V483, &V483, &V483, &V483, &V483, + &V483, &V483, &V483, &V483, &V483, &V483, &V483, &V483, + }, // P[358] + { + &V484, &V484, &V484, &V484, &V484, &V484, &V484, &V484, + &V484, &V484, &V484, &V484, &V484, &V484, &V484, &V484, + &V484, &V484, &V484, &V484, &V484, &V484, &V484, &V484, + &V484, &V484, &V484, &V484, &V484, &V484, &V484, &V484, + }, // P[359] + { + &V485, &V485, &V485, &V485, &V485, &V485, &V485, &V485, + &V485, &V485, &V485, &V485, &V485, &V485, &V485, &V485, + &V485, &V485, &V485, &V485, &V485, &V485, &V485, &V485, + &V485, &V485, &V485, &V485, &V485, &V485, &V485, &V485, + }, // P[360] + { + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + }, // P[361] + { + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V454, &V454, + &V486, &V454, &V486, &V454, &V454, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V454, + }, // P[362] + { + &V486, &V454, &V486, &V454, &V454, &V486, &V486, &V454, + &V454, &V454, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + }, // P[363] + { + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V151, &V151, + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + }, // P[364] + { + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[365] + { + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V63, &V63, &V63, &V63, &V63, + &V151, &V151, &V151, &V151, &V151, &V487, &V488, &V487, + }, // P[366] + { + &V489, &V489, &V489, &V489, &V489, &V489, &V489, &V489, + &V489, &V398, &V487, &V487, &V487, &V487, &V487, &V487, + &V487, &V487, &V487, &V487, &V487, &V487, &V487, &V151, + &V487, &V487, &V487, &V487, &V487, &V151, &V487, &V151, + }, // P[367] + { + &V487, &V487, &V151, &V487, &V487, &V151, &V487, &V487, + &V487, &V487, &V487, &V487, &V487, &V487, &V487, &V489, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + }, // P[368] + { + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + }, // P[369] + { + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V490, &V490, &V490, &V490, &V490, &V490, + &V490, &V490, &V490, &V490, &V490, &V490, &V490, &V490, + }, // P[370] + { + &V490, &V490, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + }, // P[371] + { + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V13, &V12, + }, // P[372] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + }, // P[373] + { + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V151, &V151, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + }, // P[374] + { + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[375] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V491, &V37, &V151, &V151, + }, // P[376] + { + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V383, &V383, &V383, &V383, &V383, &V383, &V383, &V401, + &V402, &V383, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[377] + { + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V138, + &V138, &V138, &V138, &V138, &V138, &V138, &V136, &V136, + &V378, &V492, &V492, &V493, &V493, &V401, &V402, &V401, + &V402, &V401, &V402, &V401, &V402, &V401, &V402, &V401, + }, // P[378] + { + &V402, &V494, &V495, &V494, &V495, &V10, &V10, &V401, + &V402, &V378, &V378, &V378, &V378, &V493, &V493, &V493, + &V496, &V383, &V496, &V151, &V383, &V496, &V383, &V383, + &V492, &V401, &V402, &V401, &V402, &V401, &V402, &V497, + }, // P[379] + { + &V378, &V378, &V398, &V498, &V400, &V400, &V400, &V151, + &V378, &V403, &V497, &V378, &V151, &V151, &V151, &V151, + &V242, &V242, &V242, &V222, &V242, &V151, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + }, // P[380] + { + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V151, &V151, &V499, + }, // P[381] + { + &V151, &V383, &V389, &V497, &V403, &V497, &V378, &V500, + &V401, &V402, &V378, &V398, &V496, &V498, &V496, &V501, + &V502, &V503, &V504, &V505, &V506, &V507, &V508, &V509, + &V510, &V511, &V496, &V383, &V400, &V400, &V400, &V383, + }, // P[382] + { + &V378, &V512, &V512, &V512, &V512, &V512, &V512, &V512, + &V512, &V512, &V512, &V512, &V512, &V512, &V512, &V512, + &V512, &V512, &V512, &V512, &V512, &V512, &V512, &V512, + &V512, &V512, &V512, &V401, &V378, &V402, &V38, &V493, + }, // P[383] + { + &V38, &V513, &V513, &V513, &V513, &V513, &V513, &V513, + &V513, &V513, &V513, &V513, &V513, &V513, &V513, &V513, + &V513, &V513, &V513, &V513, &V513, &V513, &V513, &V513, + &V513, &V513, &V513, &V401, &V400, &V402, &V400, &V401, + }, // P[384] + { + &V402, &V383, &V494, &V495, &V383, &V514, &V467, &V467, + &V467, &V467, &V467, &V467, &V467, &V467, &V467, &V467, + &V152, &V467, &V467, &V467, &V467, &V467, &V467, &V467, + &V467, &V467, &V467, &V467, &V467, &V467, &V467, &V467, + }, // P[385] + { + &V467, &V467, &V467, &V467, &V467, &V467, &V467, &V467, + &V467, &V467, &V467, &V467, &V467, &V467, &V467, &V467, + &V467, &V467, &V467, &V467, &V467, &V467, &V467, &V467, + &V467, &V467, &V467, &V467, &V467, &V467, &V152, &V152, + }, // P[386] + { + &V39, &V39, &V39, &V39, &V39, &V39, &V39, &V39, + &V39, &V39, &V39, &V39, &V39, &V39, &V39, &V39, + &V39, &V39, &V39, &V39, &V39, &V39, &V39, &V39, + &V39, &V39, &V39, &V39, &V39, &V39, &V39, &V151, + }, // P[387] + { + &V151, &V151, &V39, &V39, &V39, &V39, &V39, &V39, + &V151, &V151, &V39, &V39, &V39, &V39, &V39, &V39, + &V151, &V151, &V39, &V39, &V39, &V39, &V39, &V39, + &V151, &V151, &V39, &V39, &V39, &V151, &V151, &V151, + }, // P[388] + { + &V403, &V403, &V400, &V38, &V404, &V403, &V403, &V151, + &V404, &V400, &V400, &V400, &V400, &V404, &V404, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V515, &V515, &V515, &V37, &V37, &V151, &V151, + }, // P[389] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V151, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[390] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V151, &V84, &V84, &V151, &V84, + }, // P[391] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V151, &V151, + }, // P[392] + { + &V189, &V10, &V189, &V151, &V151, &V151, &V151, &V286, + &V286, &V286, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V286, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V286, &V286, &V286, &V286, &V286, &V286, &V286, + }, // P[393] + { + &V286, &V286, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V286, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V286, &V286, &V286, &V151, &V151, &V151, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[394] + { + &V516, &V516, &V516, &V516, &V516, &V516, &V516, &V516, + &V516, &V516, &V516, &V516, &V516, &V516, &V516, &V516, + &V516, &V516, &V516, &V516, &V516, &V516, &V516, &V516, + &V516, &V516, &V516, &V516, &V516, &V516, &V516, &V516, + }, // P[395] + { + &V516, &V516, &V516, &V516, &V516, &V516, &V516, &V516, + &V516, &V516, &V516, &V516, &V516, &V516, &V516, &V516, + &V516, &V516, &V516, &V516, &V516, &V291, &V291, &V291, + &V291, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[396] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V291, &V291, &V37, &V184, &V184, &V151, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V151, &V151, &V151, &V151, + }, // P[397] + { + &V37, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[398] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[399] + { + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V138, &V151, &V151, + }, // P[400] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V151, &V151, &V151, + }, // P[401] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[402] + { + &V138, &V517, &V517, &V517, &V517, &V517, &V517, &V517, + &V517, &V517, &V517, &V517, &V517, &V517, &V517, &V517, + &V517, &V517, &V517, &V517, &V517, &V517, &V517, &V517, + &V517, &V517, &V517, &V517, &V151, &V151, &V151, &V151, + }, // P[403] + { + &V286, &V286, &V286, &V286, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[404] + { + &V84, &V321, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V321, &V151, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[405] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V136, &V136, + &V136, &V136, &V136, &V151, &V151, &V151, &V151, &V151, + }, // P[406] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V151, &V189, + }, // P[407] + { + &V84, &V84, &V84, &V84, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V189, &V321, &V321, &V321, &V321, &V321, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[408] + { + &V518, &V518, &V518, &V518, &V518, &V518, &V518, &V518, + &V518, &V518, &V518, &V518, &V518, &V518, &V518, &V518, + &V518, &V518, &V518, &V518, &V518, &V518, &V518, &V518, + &V518, &V518, &V518, &V518, &V518, &V518, &V518, &V518, + }, // P[409] + { + &V518, &V518, &V518, &V518, &V518, &V518, &V518, &V518, + &V519, &V519, &V519, &V519, &V519, &V519, &V519, &V519, + &V519, &V519, &V519, &V519, &V519, &V519, &V519, &V519, + &V519, &V519, &V519, &V519, &V519, &V519, &V519, &V519, + }, // P[410] + { + &V519, &V519, &V519, &V519, &V519, &V519, &V519, &V519, + &V519, &V519, &V519, &V519, &V519, &V519, &V519, &V519, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[411] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V151, &V151, + }, // P[412] + { + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V151, &V151, + &V518, &V518, &V518, &V518, &V518, &V518, &V518, &V518, + &V518, &V518, &V518, &V518, &V518, &V518, &V518, &V518, + }, // P[413] + { + &V518, &V518, &V518, &V518, &V518, &V518, &V518, &V518, + &V518, &V518, &V518, &V518, &V518, &V518, &V518, &V518, + &V518, &V518, &V518, &V518, &V151, &V151, &V151, &V151, + &V519, &V519, &V519, &V519, &V519, &V519, &V519, &V519, + }, // P[414] + { + &V519, &V519, &V519, &V519, &V519, &V519, &V519, &V519, + &V519, &V519, &V519, &V519, &V519, &V519, &V519, &V519, + &V519, &V519, &V519, &V519, &V519, &V519, &V519, &V519, + &V519, &V519, &V519, &V519, &V151, &V151, &V151, &V151, + }, // P[415] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[416] + { + &V84, &V84, &V84, &V84, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V189, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[417] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[418] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V151, &V151, + &V213, &V151, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + }, // P[419] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V151, &V213, + &V213, &V151, &V151, &V151, &V213, &V151, &V151, &V213, + }, // P[420] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V151, &V210, + &V520, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + }, // P[421] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V521, + &V521, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + }, // P[422] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V151, + }, // P[423] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V520, + &V520, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[424] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V151, &V213, &V213, &V151, &V151, + &V151, &V151, &V151, &V520, &V520, &V520, &V520, &V520, + }, // P[425] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V520, &V520, + &V520, &V520, &V520, &V520, &V151, &V151, &V151, &V10, + }, // P[426] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V151, &V151, &V151, &V151, &V151, &V210, + }, // P[427] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + }, // P[428] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V151, &V151, &V151, &V151, &V520, &V520, &V213, &V213, + }, // P[429] + { + &V520, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + &V520, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + &V151, &V151, &V520, &V520, &V520, &V520, &V520, &V520, + &V520, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + }, // P[430] + { + &V520, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + &V520, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + &V520, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + &V520, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + }, // P[431] + { + &V213, &V147, &V147, &V147, &V151, &V147, &V147, &V151, + &V151, &V151, &V151, &V151, &V147, &V138, &V147, &V136, + &V213, &V213, &V213, &V213, &V151, &V213, &V213, &V213, + &V151, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + }, // P[432] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V151, &V151, &V151, &V151, + &V136, &V143, &V138, &V151, &V151, &V151, &V151, &V271, + }, // P[433] + { + &V522, &V523, &V524, &V525, &V520, &V520, &V520, &V520, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V210, &V210, &V210, &V210, &V210, &V210, &V526, &V526, + &V210, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[434] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V520, &V520, &V210, + }, // P[435] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V520, &V520, &V520, + }, // P[436] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V521, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + }, // P[437] + { + &V213, &V213, &V213, &V213, &V213, &V136, &V138, &V151, + &V151, &V151, &V151, &V520, &V520, &V520, &V520, &V520, + &V210, &V210, &V210, &V210, &V210, &V210, &V210, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[438] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V151, &V151, + &V151, &V10, &V10, &V10, &V10, &V10, &V10, &V10, + }, // P[439] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V151, &V151, + &V520, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + }, // P[440] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V151, &V151, &V151, &V151, &V151, + &V520, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + }, // P[441] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V213, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V210, &V210, &V210, &V210, &V151, &V151, &V151, + }, // P[442] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[443] + { + &V213, &V213, &V213, &V213, &V213, &V213, &V213, &V213, + &V213, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[444] + { + &V527, &V527, &V527, &V527, &V527, &V527, &V527, &V527, + &V527, &V527, &V527, &V527, &V527, &V527, &V527, &V527, + &V527, &V527, &V527, &V527, &V527, &V527, &V527, &V527, + &V527, &V527, &V527, &V527, &V527, &V527, &V527, &V527, + }, // P[445] + { + &V527, &V527, &V527, &V527, &V527, &V527, &V527, &V527, + &V527, &V527, &V527, &V527, &V527, &V527, &V527, &V527, + &V527, &V527, &V527, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[446] + { + &V528, &V528, &V528, &V528, &V528, &V528, &V528, &V528, + &V528, &V528, &V528, &V528, &V528, &V528, &V528, &V528, + &V528, &V528, &V528, &V528, &V528, &V528, &V528, &V528, + &V528, &V528, &V528, &V528, &V528, &V528, &V528, &V528, + }, // P[447] + { + &V528, &V528, &V528, &V528, &V528, &V528, &V528, &V528, + &V528, &V528, &V528, &V528, &V528, &V528, &V528, &V528, + &V528, &V528, &V528, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V520, &V520, &V520, &V520, &V520, &V520, + }, // P[448] + { + &V529, &V530, &V531, &V532, &V533, &V534, &V535, &V536, + &V537, &V538, &V538, &V538, &V538, &V538, &V538, &V538, + &V538, &V538, &V538, &V538, &V538, &V538, &V538, &V538, + &V538, &V538, &V538, &V538, &V538, &V538, &V538, &V151, + }, // P[449] + { + &V268, &V147, &V268, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[450] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + }, // P[451] + { + &V147, &V147, &V147, &V147, &V147, &V147, &V271, &V191, + &V191, &V189, &V189, &V189, &V189, &V189, &V151, &V151, + &V151, &V151, &V431, &V432, &V433, &V434, &V435, &V436, + &V437, &V438, &V439, &V291, &V291, &V291, &V291, &V291, + }, // P[452] + { + &V291, &V291, &V291, &V291, &V291, &V291, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V271, + }, // P[453] + { + &V147, &V147, &V268, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V269, &V84, &V269, &V84, &V84, &V84, + }, // P[454] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V269, &V84, &V84, &V84, &V84, + &V268, &V268, &V268, &V147, &V147, &V147, &V147, &V268, + &V268, &V271, &V270, &V189, &V189, &V539, &V189, &V189, + }, // P[455] + { + &V191, &V191, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[456] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[457] + { + &V136, &V136, &V136, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[458] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V287, + &V147, &V147, &V147, &V147, &V268, &V147, &V288, &V288, + &V147, &V147, &V147, &V271, &V271, &V151, &V273, &V274, + &V275, &V276, &V277, &V278, &V279, &V280, &V281, &V282, + }, // P[459] + { + &V189, &V191, &V191, &V191, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[460] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V283, &V189, &V189, &V84, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[461] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V268, &V268, &V268, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V268, + }, // P[462] + { + &V322, &V84, &V84, &V84, &V84, &V191, &V191, &V189, + &V189, &V189, &V283, &V147, &V147, &V189, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V84, &V189, &V84, &V189, &V189, &V189, + }, // P[463] + { + &V151, &V286, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V286, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V286, &V286, &V286, &V286, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[464] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V151, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[465] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V268, &V268, &V268, &V147, + &V147, &V147, &V268, &V268, &V147, &V322, &V283, &V147, + &V191, &V191, &V189, &V189, &V189, &V189, &V147, &V151, + }, // P[466] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V84, &V151, &V84, &V84, &V84, &V84, &V151, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V151, &V84, + }, // P[467] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V189, &V151, &V151, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[468] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V147, + }, // P[469] + { + &V268, &V268, &V268, &V147, &V147, &V147, &V147, &V147, + &V147, &V283, &V271, &V151, &V151, &V151, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[470] + { + &V147, &V147, &V268, &V268, &V151, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V151, &V151, &V84, + &V84, &V151, &V151, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[471] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V151, &V84, &V84, &V84, + &V84, &V84, &V151, &V151, &V283, &V84, &V284, &V268, + }, // P[472] + { + &V147, &V268, &V268, &V268, &V268, &V151, &V151, &V268, + &V268, &V151, &V151, &V285, &V285, &V322, &V151, &V151, + &V84, &V151, &V151, &V151, &V151, &V151, &V151, &V284, + &V151, &V151, &V151, &V151, &V151, &V84, &V84, &V84, + }, // P[473] + { + &V84, &V84, &V268, &V268, &V151, &V151, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V151, &V151, &V151, + &V136, &V136, &V136, &V136, &V136, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[474] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V268, &V268, &V268, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + }, // P[475] + { + &V268, &V268, &V271, &V147, &V147, &V268, &V283, &V84, + &V84, &V84, &V84, &V191, &V191, &V191, &V189, &V189, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V189, &V151, &V189, &V151, &V151, + }, // P[476] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V284, &V268, &V268, &V147, &V147, &V147, &V147, &V147, + &V147, &V268, &V287, &V285, &V285, &V284, &V285, &V147, + }, // P[477] + { + &V147, &V268, &V271, &V283, &V84, &V84, &V189, &V84, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[478] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V284, + &V268, &V268, &V147, &V147, &V147, &V147, &V151, &V151, + &V268, &V268, &V285, &V285, &V147, &V147, &V268, &V271, + }, // P[479] + { + &V283, &V189, &V191, &V191, &V189, &V189, &V189, &V189, + &V189, &V189, &V189, &V189, &V189, &V189, &V189, &V189, + &V189, &V189, &V189, &V189, &V189, &V189, &V189, &V189, + &V84, &V84, &V84, &V84, &V147, &V147, &V151, &V151, + }, // P[480] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V268, &V268, &V268, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V147, &V268, &V268, &V147, &V268, &V271, + }, // P[481] + { + &V147, &V191, &V191, &V189, &V84, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[482] + { + &V10, &V10, &V10, &V10, &V10, &V10, &V10, &V10, + &V10, &V10, &V10, &V10, &V10, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[483] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V147, &V268, &V147, &V268, &V268, + &V147, &V147, &V147, &V147, &V147, &V147, &V322, &V283, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[484] + { + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[485] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V151, &V151, &V151, &V147, &V147, &V147, + }, // P[486] + { + &V268, &V268, &V147, &V147, &V147, &V147, &V268, &V147, + &V147, &V147, &V147, &V271, &V151, &V151, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V286, &V286, &V189, &V189, &V189, &V184, + }, // P[487] + { + &V30, &V30, &V30, &V30, &V30, &V30, &V30, &V30, + &V30, &V30, &V30, &V30, &V30, &V30, &V30, &V30, + &V30, &V30, &V30, &V30, &V30, &V30, &V30, &V30, + &V30, &V30, &V30, &V30, &V30, &V30, &V30, &V30, + }, // P[488] + { + &V34, &V34, &V34, &V34, &V34, &V34, &V34, &V34, + &V34, &V34, &V34, &V34, &V34, &V34, &V34, &V34, + &V34, &V34, &V34, &V34, &V34, &V34, &V34, &V34, + &V34, &V34, &V34, &V34, &V34, &V34, &V34, &V34, + }, // P[489] + { + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V286, &V286, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V84, + }, // P[490] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[491] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[492] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V268, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V151, + &V147, &V147, &V147, &V147, &V147, &V147, &V268, &V540, + }, // P[493] + { + &V84, &V191, &V191, &V189, &V189, &V189, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V286, &V286, &V286, &V286, &V286, &V286, + }, // P[494] + { + &V286, &V286, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V286, &V286, &V286, &V286, &V151, &V151, &V151, + &V189, &V189, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[495] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V151, &V151, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + }, // P[496] + { + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V151, &V268, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V268, &V147, &V147, &V268, &V147, &V147, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[497] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[498] + { + &V321, &V321, &V321, &V321, &V321, &V321, &V321, &V321, + &V321, &V321, &V321, &V321, &V321, &V321, &V321, &V321, + &V321, &V321, &V321, &V321, &V321, &V321, &V321, &V321, + &V321, &V321, &V321, &V321, &V321, &V321, &V321, &V321, + }, // P[499] + { + &V321, &V321, &V321, &V321, &V321, &V321, &V321, &V321, + &V321, &V321, &V321, &V321, &V321, &V321, &V321, &V151, + &V189, &V191, &V191, &V189, &V189, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[500] + { + &V84, &V84, &V84, &V84, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[501] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[502] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[503] + { + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V151, &V151, &V151, &V191, &V191, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[504] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + }, // P[505] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V151, &V151, + &V143, &V143, &V143, &V143, &V143, &V191, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[506] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V189, + &V189, &V189, &V189, &V189, &V184, &V184, &V184, &V184, + }, // P[507] + { + &V134, &V134, &V134, &V134, &V189, &V184, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V273, &V274, &V275, &V276, &V277, &V278, &V279, &V280, + &V281, &V282, &V151, &V286, &V286, &V286, &V286, &V286, + }, // P[508] + { + &V286, &V286, &V151, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V151, &V151, &V151, &V151, &V151, &V84, &V84, &V84, + }, // P[509] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[510] + { + &V84, &V84, &V84, &V84, &V84, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V84, &V268, &V268, &V268, &V268, &V268, &V268, &V268, + &V268, &V268, &V268, &V268, &V268, &V268, &V268, &V268, + }, // P[511] + { + &V268, &V268, &V268, &V268, &V268, &V268, &V268, &V268, + &V268, &V268, &V268, &V268, &V268, &V268, &V268, &V268, + &V268, &V268, &V268, &V268, &V268, &V268, &V268, &V268, + &V268, &V268, &V268, &V268, &V268, &V268, &V268, &V151, + }, // P[512] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V147, + &V147, &V147, &V147, &V132, &V132, &V132, &V132, &V132, + &V132, &V132, &V132, &V132, &V132, &V132, &V132, &V132, + }, // P[513] + { + &V134, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[514] + { + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[515] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[516] + { + &V464, &V459, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[517] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V151, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V84, &V84, &V84, &V151, &V151, &V151, + }, // P[518] + { + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V84, &V84, &V84, &V84, &V84, &V84, &V84, &V84, + &V84, &V84, &V151, &V151, &V184, &V147, &V143, &V191, + }, // P[519] + { + &V41, &V41, &V41, &V41, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[520] + { + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[521] + { + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V151, + &V151, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[522] + { + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V541, &V541, + }, // P[523] + { + &V541, &V541, &V541, &V541, &V541, &V542, &V542, &V143, + &V143, &V143, &V184, &V184, &V184, &V543, &V542, &V542, + &V542, &V542, &V542, &V41, &V41, &V41, &V41, &V41, + &V41, &V41, &V41, &V138, &V138, &V138, &V138, &V138, + }, // P[524] + { + &V138, &V138, &V138, &V184, &V184, &V136, &V136, &V136, + &V136, &V136, &V138, &V138, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[525] + { + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V136, &V136, &V136, &V136, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V541, &V541, &V541, &V541, &V541, + }, // P[526] + { + &V541, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[527] + { + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[528] + { + &V37, &V37, &V136, &V136, &V136, &V37, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[529] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[530] + { + &V286, &V286, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V286, &V286, &V286, &V286, &V286, &V286, &V286, + &V286, &V286, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[531] + { + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V63, &V63, &V63, &V63, &V63, &V63, + }, // P[532] + { + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + }, // P[533] + { + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V151, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + }, // P[534] + { + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + }, // P[535] + { + &V168, &V168, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V168, &V151, &V168, &V168, + }, // P[536] + { + &V151, &V151, &V168, &V151, &V151, &V168, &V168, &V151, + &V151, &V168, &V168, &V168, &V168, &V151, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V63, &V63, + &V63, &V63, &V151, &V63, &V151, &V63, &V63, &V63, + }, // P[537] + { + &V63, &V63, &V63, &V63, &V151, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + }, // P[538] + { + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + }, // P[539] + { + &V63, &V63, &V63, &V63, &V168, &V168, &V151, &V168, + &V168, &V168, &V168, &V151, &V151, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V151, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V151, &V63, &V63, + }, // P[540] + { + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V168, &V168, &V151, &V168, &V168, &V168, &V168, &V151, + }, // P[541] + { + &V168, &V168, &V168, &V168, &V168, &V151, &V168, &V151, + &V151, &V151, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V151, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + }, // P[542] + { + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + }, // P[543] + { + &V168, &V168, &V168, &V168, &V168, &V168, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + }, // P[544] + { + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + }, // P[545] + { + &V168, &V168, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V168, &V168, &V168, &V168, + }, // P[546] + { + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + }, // P[547] + { + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + }, // P[548] + { + &V63, &V63, &V63, &V63, &V63, &V63, &V151, &V151, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + }, // P[549] + { + &V168, &V544, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V400, &V63, &V63, &V63, &V63, + }, // P[550] + { + &V63, &V63, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V544, &V63, &V63, &V63, &V63, + }, // P[551] + { + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V400, &V63, &V63, + &V63, &V63, &V63, &V63, &V168, &V168, &V168, &V168, + }, // P[552] + { + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V544, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + }, // P[553] + { + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V400, + &V63, &V63, &V63, &V63, &V63, &V63, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + }, // P[554] + { + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V544, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + }, // P[555] + { + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V400, &V63, &V63, &V63, &V63, &V63, &V63, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + }, // P[556] + { + &V168, &V168, &V168, &V168, &V168, &V168, &V168, &V168, + &V168, &V544, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + &V63, &V63, &V63, &V63, &V63, &V63, &V63, &V63, + }, // P[557] + { + &V63, &V63, &V63, &V400, &V63, &V63, &V63, &V63, + &V63, &V63, &V168, &V63, &V151, &V151, &V502, &V503, + &V504, &V505, &V506, &V507, &V508, &V509, &V510, &V511, + &V502, &V503, &V504, &V505, &V506, &V507, &V508, &V509, + }, // P[558] + { + &V510, &V511, &V502, &V503, &V504, &V505, &V506, &V507, + &V508, &V509, &V510, &V511, &V502, &V503, &V504, &V505, + &V506, &V507, &V508, &V509, &V510, &V511, &V502, &V503, + &V504, &V505, &V506, &V507, &V508, &V509, &V510, &V511, + }, // P[559] + { + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + }, // P[560] + { + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V184, + &V184, &V184, &V184, &V147, &V147, &V147, &V147, &V147, + }, // P[561] + { + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V147, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[562] + { + &V184, &V184, &V184, &V184, &V147, &V184, &V184, &V191, + &V191, &V191, &V191, &V189, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V147, &V147, &V147, &V147, &V147, + }, // P[563] + { + &V151, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[564] + { + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V151, + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + &V136, &V136, &V136, &V136, &V136, &V136, &V136, &V136, + &V136, &V151, &V151, &V136, &V136, &V136, &V136, &V136, + }, // P[565] + { + &V136, &V136, &V151, &V136, &V136, &V151, &V136, &V136, + &V136, &V136, &V136, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[566] + { + &V213, &V213, &V213, &V213, &V213, &V151, &V151, &V520, + &V520, &V520, &V520, &V520, &V520, &V520, &V520, &V520, + &V138, &V138, &V138, &V138, &V138, &V138, &V138, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[567] + { + &V545, &V545, &V545, &V545, &V545, &V545, &V545, &V545, + &V545, &V545, &V545, &V545, &V545, &V545, &V545, &V545, + &V545, &V545, &V545, &V545, &V545, &V545, &V545, &V545, + &V545, &V545, &V545, &V545, &V545, &V545, &V545, &V545, + }, // P[568] + { + &V545, &V545, &V546, &V546, &V546, &V546, &V546, &V546, + &V546, &V546, &V546, &V546, &V546, &V546, &V546, &V546, + &V546, &V546, &V546, &V546, &V546, &V546, &V546, &V546, + &V546, &V546, &V546, &V546, &V546, &V546, &V546, &V546, + }, // P[569] + { + &V546, &V546, &V546, &V546, &V136, &V136, &V136, &V136, + &V136, &V136, &V283, &V151, &V151, &V151, &V151, &V151, + &V256, &V257, &V258, &V259, &V260, &V261, &V262, &V263, + &V264, &V265, &V151, &V151, &V151, &V151, &V526, &V526, + }, // P[570] + { + &V242, &V242, &V242, &V242, &V151, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + }, // P[571] + { + &V151, &V242, &V242, &V151, &V242, &V151, &V151, &V242, + &V151, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V151, &V242, &V242, &V242, &V242, + &V151, &V242, &V151, &V242, &V151, &V151, &V151, &V151, + }, // P[572] + { + &V151, &V151, &V242, &V151, &V151, &V151, &V151, &V242, + &V151, &V242, &V151, &V242, &V151, &V242, &V242, &V242, + &V151, &V242, &V242, &V151, &V242, &V151, &V151, &V242, + &V151, &V242, &V151, &V242, &V151, &V242, &V151, &V242, + }, // P[573] + { + &V151, &V242, &V242, &V151, &V242, &V151, &V151, &V242, + &V242, &V242, &V242, &V151, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V151, &V242, &V242, &V242, &V242, + &V151, &V242, &V242, &V242, &V242, &V151, &V242, &V151, + }, // P[574] + { + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V151, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V151, &V151, &V151, &V151, + }, // P[575] + { + &V151, &V242, &V242, &V242, &V151, &V242, &V242, &V242, + &V242, &V242, &V151, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V242, &V242, &V242, &V242, + &V242, &V242, &V242, &V242, &V151, &V151, &V151, &V151, + }, // P[576] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V28, &V28, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[577] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V151, &V151, &V151, &V151, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[578] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[579] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V151, + &V151, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[580] + { + &V151, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V151, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[581] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[582] + { + &V391, &V391, &V48, &V44, &V45, &V392, &V393, &V394, + &V395, &V396, &V397, &V291, &V291, &V151, &V151, &V151, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + }, // P[583] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V151, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + }, // P[584] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[585] + { + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V404, &V404, &V151, &V151, &V151, &V151, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[586] + { + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V427, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[587] + { + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[588] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + &V184, &V184, &V184, &V184, &V184, &V184, &V184, &V184, + }, // P[589] + { + &V427, &V427, &V427, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + }, // P[590] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V427, &V427, &V427, &V151, &V151, &V151, &V151, + }, // P[591] + { + &V427, &V427, &V427, &V427, &V427, &V427, &V427, &V427, + &V427, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V427, &V427, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[592] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V31, &V31, &V31, &V31, &V31, + }, // P[593] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[594] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V151, &V151, &V151, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[595] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[596] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[597] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + }, // P[598] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[599] + { + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V151, + }, // P[600] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V37, &V151, &V151, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V151, + }, // P[601] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V151, &V151, &V151, &V151, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V151, + }, // P[602] + { + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V37, &V37, &V37, &V37, &V37, &V37, + &V37, &V37, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[603] + { + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[604] + { + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[605] + { + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V454, &V454, &V454, + &V454, &V454, &V454, &V454, &V454, &V454, &V151, &V151, + }, // P[606] + { + &V454, &V454, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[607] + { + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V486, &V486, + &V486, &V486, &V486, &V486, &V486, &V486, &V151, &V151, + }, // P[608] + { + &V151, &V41, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[609] + { + &V41, &V41, &V41, &V41, &V41, &V41, &V41, &V41, + &V386, &V386, &V41, &V41, &V41, &V41, &V41, &V41, + &V41, &V41, &V41, &V41, &V41, &V41, &V41, &V41, + &V41, &V41, &V41, &V41, &V41, &V41, &V41, &V41, + }, // P[610] + { + &V41, &V41, &V41, &V41, &V41, &V41, &V41, &V41, + &V41, &V41, &V41, &V41, &V41, &V41, &V41, &V41, + &V41, &V41, &V41, &V41, &V41, &V41, &V41, &V41, + &V41, &V41, &V41, &V386, &V41, &V386, &V41, &V41, + }, // P[611] + { + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V147, &V147, &V147, &V147, &V147, &V147, &V147, &V147, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + &V151, &V151, &V151, &V151, &V151, &V151, &V151, &V151, + }, // P[612] + { + &V485, &V485, &V485, &V485, &V485, &V485, &V485, &V485, + &V485, &V485, &V485, &V485, &V485, &V485, &V485, &V485, + &V485, &V485, &V485, &V485, &V485, &V485, &V485, &V485, + &V485, &V485, &V485, &V485, &V485, &V485, &V151, &V151, + }, // P[613] + { + &V485, &V485, &V485, &V485, &V485, &V485, &V485, &V485, + &V485, &V485, &V485, &V485, &V485, &V485, &V485, &V485, + &V485, &V485, &V485, &V485, &V485, &V485, &V485, &V485, + &V485, &V485, &V485, &V485, &V485, &V485, + }, // P[614] + }; // static const NUnicode::NPrivate::TUnidataTable::TValuePtr P[][32] + + static const NUnicode::NPrivate::TUnidataTable::TValuePtr* const Indexes[] = { + P[0], P[1], P[2], P[3], P[4], P[5], P[6], P[7], P[8], P[9], P[10], P[11], P[12], P[13], P[14], P[15], + P[16], P[17], P[18], P[19], P[20], P[21], P[22], P[23], P[24], P[25], P[26], P[27], P[28], P[29], P[30], P[31], + P[32], P[33], P[34], P[35], P[36], P[37], P[38], P[39], P[37], P[40], P[41], P[42], P[43], P[44], P[45], P[46], + P[47], P[48], P[49], P[50], P[51], P[51], P[52], P[53], P[54], P[55], P[56], P[51], P[51], P[57], P[58], P[59], + P[60], P[61], P[62], P[63], P[63], P[64], P[65], P[66], P[67], P[68], P[69], P[70], P[71], P[72], P[73], P[74], + P[75], P[76], P[77], P[78], P[79], P[80], P[81], P[82], P[83], P[84], P[85], P[86], P[87], P[88], P[89], P[90], + P[91], P[92], P[93], P[94], P[95], P[96], P[97], P[98], P[99], P[100], P[101], P[102], P[103], P[104], P[105], P[106], + P[107], P[108], P[109], P[63], P[110], P[111], P[112], P[63], P[113], P[114], P[115], P[116], P[117], P[118], P[119], P[63], + P[120], P[121], P[122], P[123], P[124], P[125], P[126], P[127], P[128], P[128], P[128], P[129], P[130], P[131], P[132], P[133], + P[120], P[120], P[134], P[120], P[135], P[136], P[137], P[120], P[138], P[120], P[139], P[140], P[141], P[142], P[142], P[143], + P[144], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], + P[120], P[120], P[120], P[145], P[146], P[120], P[120], P[147], P[148], P[149], P[150], P[151], P[120], P[152], P[153], P[154], + P[155], P[120], P[156], P[157], P[158], P[159], P[120], P[160], P[161], P[162], P[163], P[164], P[120], P[165], P[166], P[167], + P[168], P[120], P[169], P[170], P[171], P[172], P[63], P[63], P[173], P[174], P[175], P[176], P[177], P[178], P[120], P[179], + P[120], P[180], P[181], P[182], P[183], P[63], P[184], P[185], P[186], P[187], P[188], P[189], P[190], P[191], P[192], P[193], + P[194], P[194], P[194], P[194], P[195], P[194], P[194], P[196], P[197], P[198], P[199], P[200], P[201], P[202], P[203], P[204], + P[205], P[206], P[207], P[208], P[209], P[210], P[211], P[212], P[213], P[214], P[215], P[216], P[217], P[218], P[219], P[220], + P[221], P[222], P[223], P[224], P[225], P[226], P[227], P[228], P[229], P[230], P[231], P[232], P[233], P[234], P[235], P[236], + P[167], P[237], P[238], P[239], P[240], P[241], P[242], P[243], P[167], P[167], P[167], P[167], P[167], P[244], P[245], P[246], + P[167], P[167], P[167], P[247], P[167], P[248], P[167], P[167], P[167], P[167], P[167], P[249], P[250], P[167], P[251], P[252], + P[231], P[231], P[231], P[231], P[231], P[231], P[231], P[231], P[227], P[227], P[227], P[227], P[253], P[227], P[254], P[255], + P[256], P[227], P[227], P[257], P[227], P[227], P[258], P[227], P[167], P[259], P[260], P[261], P[262], P[263], P[264], P[265], + P[266], P[267], P[268], P[269], P[37], P[37], P[37], P[270], P[271], P[272], P[120], P[273], P[274], P[275], P[275], P[276], + P[277], P[278], P[279], P[63], P[280], P[167], P[167], P[281], P[282], P[282], P[282], P[282], P[282], P[282], P[283], P[284], + P[285], P[286], P[287], P[288], P[289], P[290], P[291], P[292], P[293], P[294], P[295], P[295], P[296], P[297], P[167], P[298], + P[299], P[300], P[301], P[302], P[300], P[303], P[304], P[305], P[306], P[306], P[306], P[307], P[306], P[306], P[308], P[309], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[311], P[167], P[167], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[311], P[63], + P[312], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], + P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], + P[120], P[120], P[120], P[120], P[313], P[167], P[314], P[315], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], + P[316], P[317], P[37], P[318], P[319], P[120], P[120], P[320], P[321], P[322], P[37], P[323], P[324], P[325], P[63], P[326], + P[327], P[328], P[120], P[329], P[330], P[331], P[332], P[333], P[334], P[335], P[336], P[337], P[67], P[338], P[339], P[340], + P[120], P[341], P[342], P[343], P[120], P[344], P[345], P[346], P[347], P[348], P[349], P[350], P[351], P[351], P[120], P[352], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], + P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[353], P[354], P[355], P[356], + P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], + P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[357], P[358], P[358], P[358], P[358], + P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], + P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], P[359], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[361], P[361], P[361], P[361], P[361], P[361], P[361], P[361], + P[362], P[363], P[361], P[364], P[361], P[361], P[365], P[63], P[366], P[367], P[368], P[369], P[369], P[370], P[371], P[369], + P[369], P[369], P[369], P[369], P[369], P[369], P[369], P[369], P[369], P[372], P[373], P[369], P[374], P[369], P[375], P[376], + P[377], P[378], P[379], P[380], P[369], P[369], P[369], P[381], P[382], P[383], P[384], P[385], P[386], P[387], P[388], P[389], + P[390], P[391], P[392], P[63], P[120], P[120], P[120], P[297], P[393], P[394], P[395], P[396], P[397], P[398], P[399], P[400], + P[63], P[63], P[63], P[63], P[401], P[120], P[402], P[403], P[120], P[404], P[405], P[406], P[407], P[120], P[408], P[63], + P[409], P[410], P[411], P[120], P[412], P[413], P[414], P[415], P[120], P[416], P[120], P[417], P[63], P[63], P[63], P[63], + P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[274], P[160], P[418], P[63], P[63], P[63], P[63], + P[419], P[420], P[421], P[422], P[423], P[424], P[63], P[425], P[426], P[427], P[63], P[63], P[428], P[429], P[430], P[431], + P[432], P[433], P[434], P[435], P[436], P[63], P[437], P[438], P[428], P[439], P[440], P[441], P[442], P[443], P[63], P[63], + P[428], P[428], P[444], P[63], P[445], P[446], P[447], P[448], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[449], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[450], P[451], P[452], P[453], P[454], P[455], P[456], P[457], P[458], P[459], P[460], P[461], P[177], P[462], P[463], P[464], + P[465], P[466], P[63], P[63], P[467], P[468], P[469], P[470], P[471], P[472], P[473], P[474], P[63], P[63], P[63], P[63], + P[120], P[475], P[476], P[63], P[120], P[477], P[478], P[63], P[63], P[63], P[63], P[63], P[120], P[479], P[480], P[63], + P[120], P[481], P[482], P[483], P[120], P[484], P[485], P[63], P[486], P[487], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[488], P[489], P[490], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[120], P[491], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[492], P[493], P[494], P[495], P[496], P[497], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], + P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[498], P[63], P[63], P[63], + P[499], P[499], P[499], P[500], P[120], P[120], P[120], P[120], P[120], P[120], P[501], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], + P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], + P[120], P[502], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], + P[120], P[120], P[503], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], + P[120], P[491], P[161], P[504], P[63], P[63], P[505], P[506], P[120], P[507], P[508], P[509], P[510], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[120], P[120], P[511], P[512], P[513], P[63], P[63], P[514], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[515], + P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[120], + P[120], P[120], P[120], P[120], P[120], P[120], P[120], P[516], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[517], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[120], P[120], P[120], P[518], P[519], P[520], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[231], P[231], P[231], P[231], P[231], P[231], P[231], P[521], P[231], P[522], P[523], P[524], P[525], P[526], P[527], P[528], + P[167], P[167], P[529], P[63], P[63], P[63], P[63], P[63], P[167], P[167], P[530], P[531], P[63], P[63], P[63], P[63], + P[532], P[533], P[534], P[535], P[536], P[537], P[538], P[539], P[540], P[541], P[542], P[543], P[544], P[532], P[533], P[545], + P[535], P[546], P[547], P[548], P[539], P[549], P[550], P[551], P[552], P[553], P[554], P[555], P[556], P[557], P[558], P[559], + P[231], P[231], P[231], P[231], P[231], P[231], P[231], P[231], P[231], P[231], P[231], P[231], P[231], P[231], P[231], P[231], + P[560], P[561], P[560], P[562], P[563], P[564], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[565], P[566], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[428], P[428], P[428], P[428], P[428], P[428], P[567], P[63], P[568], P[569], P[570], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[571], P[572], P[573], P[574], P[575], P[576], P[63], P[577], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[167], P[578], P[167], P[167], P[579], P[580], P[581], P[582], P[583], P[584], P[585], P[586], P[587], P[588], P[63], P[589], + P[590], P[591], P[592], P[63], P[63], P[63], P[63], P[63], P[167], P[167], P[167], P[167], P[167], P[167], P[167], P[593], + P[167], P[167], P[167], P[167], P[167], P[167], P[167], P[167], P[167], P[167], P[167], P[167], P[167], P[167], P[167], P[167], + P[167], P[167], P[167], P[167], P[167], P[167], P[594], P[595], P[167], P[167], P[167], P[579], P[167], P[167], P[596], P[63], + P[578], P[167], P[597], P[167], P[598], P[599], P[63], P[63], P[600], P[601], P[602], P[63], P[603], P[63], P[398], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[604], P[63], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[605], P[310], P[310], P[310], P[310], P[310], P[310], + P[606], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], P[310], + P[310], P[310], P[310], P[310], P[310], P[607], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[361], P[361], P[361], P[361], P[361], P[361], P[361], P[361], P[361], P[361], P[361], P[361], P[361], P[361], P[361], P[361], + P[608], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[609], P[610], P[611], P[611], P[63], P[63], P[63], P[63], P[560], P[560], P[560], P[560], P[560], P[560], P[560], P[612], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], P[63], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[613], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], + P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[360], P[614], + }; // static const NUnicode::NPrivate::TUnidataTable::TValuePtr* const Indexes[] + + static const size_t Size = 1114110; +}} // namespace NUnidataTableGenerated + +namespace NUnicode { + namespace NPrivate { + const NUnicode::NPrivate::TUnidataTable& UnidataTable() { + static const NUnicode::NPrivate::TUnidataTable data(NUnidataTableGenerated::Indexes, NUnidataTableGenerated::Size); + return data; + } + } // namespace NPrivate +} // namespace NUnicode +namespace NUnicode { + namespace NPrivate { + const size_t DEFAULT_KEY = 0xE001; + static_assert(NUnidataTableGenerated::Size > DEFAULT_KEY, "table size should be greater then default key"); + } +} diff --git a/util/charset/recode_result.cpp b/util/charset/recode_result.cpp new file mode 100644 index 00000000000..23638027bb6 --- /dev/null +++ b/util/charset/recode_result.cpp @@ -0,0 +1 @@ +#include "recode_result.h" diff --git a/util/charset/recode_result.h b/util/charset/recode_result.h new file mode 100644 index 00000000000..2c7366e7b27 --- /dev/null +++ b/util/charset/recode_result.h @@ -0,0 +1,9 @@ +#pragma once + +enum RECODE_RESULT { + RECODE_OK, + RECODE_EOINPUT, + RECODE_EOOUTPUT, + RECODE_BROKENSYMBOL, + RECODE_ERROR, +}; diff --git a/util/charset/unicode_table.cpp b/util/charset/unicode_table.cpp new file mode 100644 index 00000000000..574c50d9ece --- /dev/null +++ b/util/charset/unicode_table.cpp @@ -0,0 +1 @@ +#include "unicode_table.h" diff --git a/util/charset/unicode_table.h b/util/charset/unicode_table.h new file mode 100644 index 00000000000..9e171b25832 --- /dev/null +++ b/util/charset/unicode_table.h @@ -0,0 +1,123 @@ +#pragma once + +#include +#include +#include + +namespace NUnicodeTable { + template + struct TValueSelector; + + template + struct TValueSelector { + using TStored = const Value; + using TValueRef = const Value&; + using TValuePtr = const Value*; + + static inline TValueRef Get(TValuePtr val) { + return *val; + } + }; + + template + struct TValueSelector { + using TStored = const Value[]; + using TValueRef = const Value*; + using TValuePtr = const Value*; + + static inline TValueRef Get(TValuePtr val) { + return val; + } + }; + + template + struct TValues { + using TSelector = TValueSelector; + + using TStored = typename TSelector::TStored; + using TValueRef = typename TSelector::TValueRef; + using TValuePtr = typename TSelector::TValuePtr; + + using TData = const TValuePtr*; + + static inline TValuePtr Get(TData table, size_t index) { + static_assert(std::is_pointer::value, "expect std::is_pointer::value"); + return table[index]; + } + + static inline TValueRef Get(TValuePtr val) { + return TSelector::Get(val); + } + }; + + template + struct TSubtable { + using TStored = typename TChild::TStored; + using TValueRef = typename TChild::TValueRef; + using TValuePtr = typename TChild::TValuePtr; + using TData = const typename TChild::TData*; + + static inline TValuePtr Get(TData table, size_t key) { + static_assert(std::is_pointer::value, "expect std::is_pointer::value"); + return TChild::Get(table[key >> Shift], key & ((1 << Shift) - 1)); + } + + static inline TValueRef Get(TValuePtr val) { + return TChild::Get(val); + } + }; + + template + class TTable { + private: + using TImpl = T; + using TData = typename TImpl::TData; + + const TData Data; + const size_t MSize; + + public: + using TStored = typename TImpl::TStored; + using TValueRef = typename TImpl::TValueRef; + using TValuePtr = typename TImpl::TValuePtr; + + private: + inline TValueRef GetImpl(size_t key) const { + TValuePtr val = TImpl::Get(Data, key); + + return TImpl::Get(val); + } + + inline TValueRef Get(size_t key) const { + return GetImpl(key); + } + + public: + TTable(TData data, size_t size) + : Data(data) + , MSize(size) + { + static_assert(std::is_pointer::value, "expect std::is_pointer::value"); + } + + inline TValueRef Get(size_t key, TValueRef value) const { + if (key >= Size()) + return value; + + return GetImpl(key); + } + + inline TValueRef Get(size_t key, size_t defaultKey) const { + if (key >= Size()) + return Get(defaultKey); + + return GetImpl(key); + } + + inline size_t Size() const { + return MSize; + } + }; + + const size_t UNICODE_TABLE_SHIFT = 5; +} diff --git a/util/charset/unidata.cpp b/util/charset/unidata.cpp new file mode 100644 index 00000000000..db4dc126abd --- /dev/null +++ b/util/charset/unidata.cpp @@ -0,0 +1 @@ +#include "unidata.h" diff --git a/util/charset/unidata.h b/util/charset/unidata.h new file mode 100644 index 00000000000..400d3141868 --- /dev/null +++ b/util/charset/unidata.h @@ -0,0 +1,421 @@ +#pragma once + +#include "unicode_table.h" + +#include // wchar32, ui64, ULL() + +enum WC_TYPE { // TODO move no NUnicode + Lu_UPPER = 1, // 'Ъ' + Ll_LOWER = 2, // 'ъ' + Lt_TITLE = 3, // 'Ъ' + Lm_EXTENDER = 4, // '-' + Lm_LETTER = 5, // 'ъ' + Lo_OTHER = 6, // '?' + Lo_IDEOGRAPH = 7, // '?' + Lo_KATAKANA = 8, // '?' + Lo_HIRAGANA = 9, // '?' + Lo_LEADING = 10, // '?' + Lo_VOWEL = 11, // '?' + Lo_TRAILING = 12, // '?' + + Mn_NONSPACING = 13, // '`' + Me_ENCLOSING = 14, // '`' + Mc_SPACING = 15, // '`' + + Nd_DIGIT = 16, // '9' // convert to digit + Nl_LETTER = 17, // 'X' // X,V,C,L,I ... + Nl_IDEOGRAPH = 18, // '?' + No_OTHER = 19, // '9' + + Zs_SPACE = 20, // ' ' [\40\240] SPACE ... NO-BREAK SPACE (00A0) + Zs_ZWSPACE = 21, // ' ' // nothing ? + Zl_LINE = 22, // '\n' + Zp_PARAGRAPH = 23, // '\n' + + Cc_ASCII = 24, // '\x1A' // can not happen + Cc_SPACE = 25, // '\x1A' // can not happen + Cc_SEPARATOR = 26, // '\x1A' // can not happen + + Cf_FORMAT = 27, // '\x1A' // nothing ? + Cf_JOIN = 28, // '\x1A' // nothing ? + Cf_BIDI = 29, // '\x1A' // nothing ? + Cf_ZWNBSP = 30, // '\x1A' // nothing ? + + Cn_UNASSIGNED = 0, // '?' + Co_PRIVATE = 0, // '?' + Cs_LOW = 31, // '?' + Cs_HIGH = 32, // '?' + + Pd_DASH = 33, // '-' + Pd_HYPHEN = 34, // '-' [-] HYPHEN-MINUS + Ps_START = 35, // '(' [([{] LEFT PARENTHESIS ... LEFT CURLY BRACKET + Ps_QUOTE = 36, // '"' + Pe_END = 37, // ')' [)]}] RIGHT PARENTHESIS ... RIGHT CURLY BRACKET + Pe_QUOTE = 38, // '"' + Pi_QUOTE = 39, // '"' + Pf_QUOTE = 40, // '"' + Pc_CONNECTOR = 41, // '_' [_] LOW LINE + Po_OTHER = 42, // '*' [#%&*/@\] NUMBER SIGN ... REVERSE SOLIDUS + Po_QUOTE = 43, // '"' ["] QUOTATION MARK + Po_TERMINAL = 44, // '.' [!,.:;?] EXCLAMATION MARK ... QUESTION MARK + Po_EXTENDER = 45, // '-' [№] MIDDLE DOT (00B7) + Po_HYPHEN = 46, // '-' + + Sm_MATH = 47, // '=' [+<=>|~] PLUS SIGN ... TILDE + Sm_MINUS = 48, // '-' + Sc_CURRENCY = 49, // '$' [$] DOLLAR SIGN + Sk_MODIFIER = 50, // '`' [^`] CIRCUMFLEX ACCENT ... GRAVE ACCENT + So_OTHER = 51, // '°' [°] DEGREE SIGN (00B0) + + Ps_SINGLE_QUOTE = 52, // '\'' ['] OPENING SINGLE QUOTE + Pe_SINGLE_QUOTE = 53, // '\'' ['] CLOSING SINGLE QUOTE + Pi_SINGLE_QUOTE = 54, // '\'' ['] INITIAL SINGLE QUOTE + Pf_SINGLE_QUOTE = 55, // '\'' ['] FINAL SINGLE QUOTE + Po_SINGLE_QUOTE = 56, // '\'' ['] APOSTROPHE and PRIME + + CCL_NUM = 57, + CCL_MASK = 0x3F, + + IS_ASCII_XDIGIT = 1 << 6, + IS_DIGIT = 1 << 7, + IS_NONBREAK = 1 << 8, + + IS_PRIVATE = 1 << 9, + + IS_COMPAT = 1 << 10, + IS_CANON = 1 << 11, + + NFD_QC = 1 << 12, + NFC_QC = 1 << 13, + NFKD_QC = 1 << 14, + NFKC_QC = 1 << 15, + + BIDI_OFFSET = 16, + SVAL_OFFSET = 22, +}; + +const size_t DEFCHAR_BUF = 58; // CCL_NUM + 1 + +#define SHIFT(i) (ULL(1) << (i)) + +namespace NUnicode { + using TCombining = ui8; + + namespace NPrivate { + struct TProperty { + ui32 Info; + i32 Lower; + i32 Upper; + i32 Title; + TCombining Combining; + }; + + extern const size_t DEFAULT_KEY; + + using TUnidataTable = NUnicodeTable::TTable>>; + const TUnidataTable& UnidataTable(); + + inline const TProperty& CharProperty(wchar32 ch) { + return UnidataTable().Get(ch, DEFAULT_KEY); + } + + inline ui32 CharInfo(wchar32 ch) { + return CharProperty(ch).Info; + } + + inline bool IsBidi(wchar32 ch, ui32 type) { + return ((NUnicode::NPrivate::CharInfo(ch) >> BIDI_OFFSET) & 15) == type; + } + } + + inline size_t UnicodeInstancesLimit() { + return NPrivate::UnidataTable().Size(); + } + + inline TCombining DecompositionCombining(wchar32 ch) { + return NPrivate::CharProperty(ch).Combining; + } + + inline WC_TYPE CharType(wchar32 ch) { + return (WC_TYPE)(NUnicode::NPrivate::CharInfo(ch) & CCL_MASK); + } + inline bool CharHasType(wchar32 ch, ui64 type_bits) { + return (SHIFT(NUnicode::CharType(ch)) & type_bits) != 0; + } +} + +// all usefull properties + +inline bool IsComposed(wchar32 ch) { + return NUnicode::NPrivate::CharInfo(ch) & (IS_COMPAT | IS_CANON); +} +inline bool IsCanonComposed(wchar32 ch) { + return NUnicode::NPrivate::CharInfo(ch) & IS_CANON; +} +inline bool IsCompatComposed(wchar32 ch) { + return NUnicode::NPrivate::CharInfo(ch) & IS_COMPAT; +} + +inline bool IsWhitespace(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Cc_SPACE) | SHIFT(Zs_SPACE) | SHIFT(Zs_ZWSPACE) | SHIFT(Zl_LINE) | SHIFT(Zp_PARAGRAPH)); +} +inline bool IsAsciiCntrl(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Cc_ASCII) | SHIFT(Cc_SPACE) | SHIFT(Cc_SEPARATOR)); +} +inline bool IsBidiCntrl(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Cf_BIDI)); +} +inline bool IsJoinCntrl(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Cf_JOIN)); +} +inline bool IsFormatCntrl(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Cf_FORMAT)); +} +inline bool IsIgnorableCntrl(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Cf_FORMAT) | SHIFT(Cf_JOIN) | SHIFT(Cf_BIDI) | SHIFT(Cf_ZWNBSP)); +} +inline bool IsCntrl(wchar32 ch) { + return NUnicode::CharHasType(ch, + SHIFT(Cf_FORMAT) | SHIFT(Cf_JOIN) | SHIFT(Cf_BIDI) | SHIFT(Cf_ZWNBSP) | + SHIFT(Cc_ASCII) | SHIFT(Cc_SPACE) | SHIFT(Cc_SEPARATOR)); +} +inline bool IsZerowidth(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Cf_FORMAT) | SHIFT(Cf_JOIN) | SHIFT(Cf_BIDI) | SHIFT(Cf_ZWNBSP) | SHIFT(Zs_ZWSPACE)); +} +inline bool IsLineSep(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Zl_LINE)); +} +inline bool IsParaSep(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Zp_PARAGRAPH)); +} +inline bool IsDash(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Pd_DASH) | SHIFT(Pd_HYPHEN) | SHIFT(Sm_MINUS)); +} +inline bool IsHyphen(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Pd_HYPHEN) | SHIFT(Po_HYPHEN)); +} +inline bool IsQuotation(wchar32 ch) { + return NUnicode::CharHasType(ch, + SHIFT(Po_QUOTE) | SHIFT(Ps_QUOTE) | SHIFT(Pe_QUOTE) | SHIFT(Pi_QUOTE) | + SHIFT(Pf_QUOTE) | SHIFT(Po_SINGLE_QUOTE) | SHIFT(Ps_SINGLE_QUOTE) | + SHIFT(Pe_SINGLE_QUOTE) | SHIFT(Pi_SINGLE_QUOTE) | SHIFT(Pf_SINGLE_QUOTE)); +} + +inline bool IsSingleQuotation(wchar32 ch) { + return NUnicode::CharHasType(ch, + SHIFT(Po_SINGLE_QUOTE) | SHIFT(Ps_SINGLE_QUOTE) | SHIFT(Pe_SINGLE_QUOTE) | + SHIFT(Pi_SINGLE_QUOTE) | SHIFT(Pf_SINGLE_QUOTE)); +} + +inline bool IsTerminal(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Po_TERMINAL)); +} +inline bool IsPairedPunct(wchar32 ch) { + return NUnicode::CharHasType(ch, + SHIFT(Ps_START) | SHIFT(Pe_END) | SHIFT(Ps_QUOTE) | SHIFT(Pe_QUOTE) | + SHIFT(Pi_QUOTE) | SHIFT(Pf_QUOTE) | SHIFT(Ps_SINGLE_QUOTE) | + SHIFT(Pe_SINGLE_QUOTE) | SHIFT(Pi_SINGLE_QUOTE) | SHIFT(Pf_SINGLE_QUOTE)); +} +inline bool IsLeftPunct(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Ps_START) | SHIFT(Ps_QUOTE) | SHIFT(Ps_SINGLE_QUOTE)); +} +inline bool IsRightPunct(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Pe_END) | SHIFT(Pe_QUOTE) | SHIFT(Pe_SINGLE_QUOTE)); +} +inline bool IsCombining(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Mc_SPACING) | SHIFT(Mn_NONSPACING) | SHIFT(Me_ENCLOSING)); +} +inline bool IsNonspacing(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Mn_NONSPACING) | SHIFT(Me_ENCLOSING)); +} +inline bool IsAlphabetic(wchar32 ch) { + return NUnicode::CharHasType(ch, + SHIFT(Lu_UPPER) | SHIFT(Ll_LOWER) | SHIFT(Lt_TITLE) | SHIFT(Lm_EXTENDER) | SHIFT(Lm_LETTER) | SHIFT(Lo_OTHER) | SHIFT(Nl_LETTER)); +} +inline bool IsIdeographic(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Lo_IDEOGRAPH) | SHIFT(Nl_IDEOGRAPH)); +} +inline bool IsKatakana(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Lo_KATAKANA)); +} +inline bool IsHiragana(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Lo_HIRAGANA)); +} +inline bool IsHangulLeading(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Lo_LEADING)); +} +inline bool IsHangulVowel(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Lo_VOWEL)); +} +inline bool IsHangulTrailing(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Lo_TRAILING)); +} +inline bool IsHexdigit(wchar32 ch) { + return NUnicode::NPrivate::CharInfo(ch) & IS_ASCII_XDIGIT; +} +inline bool IsDecdigit(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Nd_DIGIT)); +} +inline bool IsNumeric(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Nd_DIGIT) | SHIFT(Nl_LETTER) | SHIFT(Nl_IDEOGRAPH) | SHIFT(No_OTHER)); +} +inline bool IsCurrency(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Sc_CURRENCY)); +} +inline bool IsMath(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Sm_MATH)); +} +inline bool IsSymbol(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Sm_MATH) | SHIFT(Sm_MINUS) | SHIFT(Sc_CURRENCY) | SHIFT(Sk_MODIFIER) | SHIFT(So_OTHER)); +} +inline bool IsLowSurrogate(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Cs_LOW)); +} +inline bool IsHighSurrogate(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Cs_HIGH)); +} +inline bool IsNonbreak(wchar32 ch) { + return NUnicode::NPrivate::CharInfo(ch) & IS_NONBREAK; +} +inline bool IsPrivate(wchar32 ch) { + return (NUnicode::NPrivate::CharInfo(ch) & IS_PRIVATE) && !NUnicode::CharHasType(ch, SHIFT(Cs_HIGH)); +} +inline bool IsUnassigned(wchar32 ch) { + return (NUnicode::CharType(ch) == 0) && !(NUnicode::NPrivate::CharInfo(ch) & IS_PRIVATE); +} +inline bool IsPrivateHighSurrogate(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Cs_HIGH)) && (NUnicode::NPrivate::CharInfo(ch) & IS_PRIVATE); +} + +// transformations + +inline wchar32 ToLower(wchar32 ch) { + return static_cast(ch + NUnicode::NPrivate::CharProperty(ch).Lower); +} +inline wchar32 ToUpper(wchar32 ch) { + return static_cast(ch + NUnicode::NPrivate::CharProperty(ch).Upper); +} +inline wchar32 ToTitle(wchar32 ch) { + return static_cast(ch + NUnicode::NPrivate::CharProperty(ch).Title); +} + +inline int ToDigit(wchar32 ch) { + ui32 i = NUnicode::NPrivate::CharInfo(ch); + return (i & IS_DIGIT) ? static_cast(i >> SVAL_OFFSET) : -1; +} + +// BIDI properties + +inline bool IsBidiLeft(wchar32 ch) { + return NUnicode::NPrivate::IsBidi(ch, 1); +} +inline bool IsBidiRight(wchar32 ch) { + return NUnicode::NPrivate::IsBidi(ch, 2); +} +inline bool IsBidiEuronum(wchar32 ch) { + return NUnicode::NPrivate::IsBidi(ch, 3); +} +inline bool IsBidiEurosep(wchar32 ch) { + return NUnicode::NPrivate::IsBidi(ch, 4); +} +inline bool IsBidiEuroterm(wchar32 ch) { + return NUnicode::NPrivate::IsBidi(ch, 5); +} +inline bool IsBidiArabnum(wchar32 ch) { + return NUnicode::NPrivate::IsBidi(ch, 6); +} +inline bool IsBidiCommsep(wchar32 ch) { + return NUnicode::NPrivate::IsBidi(ch, 7); +} +inline bool IsBidiBlocksep(wchar32 ch) { + return NUnicode::NPrivate::IsBidi(ch, 8); +} +inline bool IsBidiSegmsep(wchar32 ch) { + return NUnicode::NPrivate::IsBidi(ch, 9); +} +inline bool IsBidiSpace(wchar32 ch) { + return NUnicode::NPrivate::IsBidi(ch, 10); +} +inline bool IsBidiNeutral(wchar32 ch) { + return NUnicode::NPrivate::IsBidi(ch, 11); +} +inline bool IsBidiNotappl(wchar32 ch) { + return NUnicode::NPrivate::IsBidi(ch, 0); +} + +inline bool IsSpace(wchar32 ch) { + return IsWhitespace(ch); +} +inline bool IsLower(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Ll_LOWER)); +} +inline bool IsUpper(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Lu_UPPER)); +} +inline bool IsTitle(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Lt_TITLE)); +} +inline bool IsAlpha(wchar32 ch) { + return NUnicode::CharHasType(ch, + SHIFT(Lu_UPPER) | SHIFT(Ll_LOWER) | SHIFT(Lt_TITLE) | SHIFT(Lm_LETTER) | SHIFT(Lm_EXTENDER) | + SHIFT(Lo_OTHER) | SHIFT(Lo_IDEOGRAPH) | SHIFT(Lo_KATAKANA) | SHIFT(Lo_HIRAGANA) | + SHIFT(Lo_LEADING) | SHIFT(Lo_VOWEL) | SHIFT(Lo_TRAILING)); +} +inline bool IsAlnum(wchar32 ch) { + return NUnicode::CharHasType(ch, + SHIFT(Lu_UPPER) | SHIFT(Ll_LOWER) | SHIFT(Lt_TITLE) | SHIFT(Lm_LETTER) | SHIFT(Lm_EXTENDER) | + SHIFT(Lo_OTHER) | SHIFT(Lo_IDEOGRAPH) | SHIFT(Lo_KATAKANA) | SHIFT(Lo_HIRAGANA) | + SHIFT(Lo_LEADING) | SHIFT(Lo_VOWEL) | SHIFT(Lo_TRAILING) | + SHIFT(Nd_DIGIT) | SHIFT(Nl_LETTER) | SHIFT(Nl_IDEOGRAPH) | SHIFT(No_OTHER)); +} +inline bool IsPunct(wchar32 ch) { + return NUnicode::CharHasType(ch, + SHIFT(Pd_DASH) | + SHIFT(Pd_HYPHEN) | SHIFT(Ps_START) | SHIFT(Ps_QUOTE) | SHIFT(Pe_END) | SHIFT(Pe_QUOTE) | SHIFT(Pc_CONNECTOR) | + SHIFT(Po_OTHER) | SHIFT(Po_QUOTE) | SHIFT(Po_TERMINAL) | SHIFT(Po_EXTENDER) | SHIFT(Po_HYPHEN) | + SHIFT(Pi_QUOTE) | SHIFT(Pf_QUOTE)); +} +inline bool IsXdigit(wchar32 ch) { + return IsHexdigit(ch); +} +inline bool IsDigit(wchar32 ch) { + return IsDecdigit(ch); +} + +inline bool IsCommonDigit(wchar32 ch) { + // IsDigit returns true for some exotic symbols like "VAI DIGIT TWO" (U+A622) + // and cannot be used safely with FromString() convertors + const wchar32 ZERO = '0'; + const wchar32 NINE = '9'; + return ch >= ZERO && ch <= NINE; +} + +inline bool IsGraph(wchar32 ch) { + return IsAlnum(ch) || IsPunct(ch) || IsSymbol(ch); +} +inline bool IsBlank(wchar32 ch) { + return NUnicode::CharHasType(ch, SHIFT(Zs_SPACE) | SHIFT(Zs_ZWSPACE)) || ch == '\t'; +} +inline bool IsPrint(wchar32 ch) { + return IsAlnum(ch) || IsPunct(ch) || IsSymbol(ch) || IsBlank(ch); +} + +inline bool IsRomanDigit(wchar32 ch) { + if (NUnicode::CharHasType(ch, SHIFT(Nl_LETTER)) && 0x2160 <= ch && ch <= 0x2188) + return true; + if (ch < 127) { + switch (static_cast(::ToLower(ch))) { + case 'i': + case 'v': + case 'x': + case 'l': + case 'c': + case 'd': + case 'm': + return true; + } + } + return false; +} + +#undef SHIFT diff --git a/util/charset/ut/utf8/invalid_UTF8.bin b/util/charset/ut/utf8/invalid_UTF8.bin new file mode 100644 index 0000000000000000000000000000000000000000..3972ab163e694bec88c49bf87b60c8b927d4c66f GIT binary patch literal 419640 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

-<(AZvov^*f_OAlm@#5)XiJW~xfe>f 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 z zTaK7AZS%ZQYnyagKEGFkX`6OU|84W|9~zxL+5VS#i+^p=VDYc>cTO8oXYSr5J@?FS zIj70guFdBjY<09_gJEMQt{8o+%htog#~hz@vfYV2HAdEKw!BII&10MWK6`(o7CVkN z?%A)`oZaIWb?Vi2_@L1{4~<_swEfBD8gL$v-YTQ>W3QV@o@9 zpSFHM^GBu%b8zJ|2}uy z$sfDa?A)qZ-6J~|)&HsGtfl>i{yOORz*#lAZ5+^lO^ZK{4eBzh{*XC`TJNmW_SCjn zYns>YIy zD<{|L(XrOFUq+4kb?u6#d$!GL)OGyM2IEH7?$f;9=A{FUcImwCmnMgM*XnR+MyDpn zPdENy$%wV9x=ft3WW|X;hHRO1YG&Jsjk;`UJL``r-3B$@+V?=~!+$RSt?`lOTQ)aX zzQ56u`iqDDux0Ji=4~fU-Zp;2>hUXfuRJvM*wK}TrVJa|YS+&l8c%IFY03U3QAdi#2gty_EF;-9uG-o9^0pCb+W>{-5WUyBA!e{QvHa*I=YPaJGnV_L@vLw7gc zKWyXtO=r4|ud#o2y<=y(wphGmcGuxQ46Z%8(a04|y3}YfVAzzo6KYRv+iCX1`J)H7 zZ`-#?yRn^roU&%b!mi6t);>~e9(f#>H4SU z_MQJn;|+iOIekupEeHG08PWE~lY<7$Xf^rxv^gj1%zLlZF03)AN9|9%cgV#{ zqEI=EM6uK>V-RM_GMqF}FcyvhPnxK`HaXCW+1N>hu3Xj&8G^*;EYT)nggYlaFU57Lll22q9^WgI0)g~U)AOQ5LLfHVeHmF`!8kcmPOvlx~u0jMK=+hI`=Jx016^j@eoXOPOk!3=>014cH)#Ph1?7#cF@W9NbIkCN#y2Lu-8u-Ps*e-ho>X` zmI?CqfV7JE%}6h{R(V7Sr3W}f|CJyejRkU)7DODu?@ZP^kBU6>wP1~3OSEmfGytZ_y*EDS%i722$D$=>Iu->#xSLwOt!`cPQN5-QC^Y-QA&Z z4G@A8NJxMLNPq;F0ArN9cTw7P_CEXF_V(F(34g%%^f&s)>QMzNYt1z?pZmJ*3CJHD zO<&&)uUw;Jlk*S=-9Z?aJY;8Y4%;X7;j1H)2xV^{LYy>geU*prk5N%FOf1<)+jrwL zdkEcMhN1;38W?Koj+|VBXyW!Fvui$?ijlYtz`p zF$o(xhZqB60H24D&o*fW8(LxFY)C>MuUz26gjsBwzK1Qr$FecO!47&UD07g7tsX&S zdFTM$=Rz=KcM+#@d$GwQc#xMau(N9#ehs(81!ehe?EDl`ynS~Q=kw?+vkzGTmFo2_ zgrT_z=V|~D<%>VaD>!E!ZlZ~8j82)uIAi1Z*3!w{*xV+M@GJ;*f& zc+YtlmDBbh{{_U@{v5EeOOOzl!&li?D7AMMrPBY*4+D9BID)OPhta(?IzF(xhOwE1 ze@`)iBOoKJ{`G&a4_00Yi28|2TEad3m3>LT(barI#p9hdgWZe80vLTxbBM{G8 z#kMA9@yS6LDANwG%eixWf-{5=w;AZr`8Kk@#QEno1z$S8K}VJck-hC9Wao4ZBS3ci zW{QJQhDNYC(mq0&UPI2vSHQoIq5FGmBoq3{rJ+tDL%9| zg`X{N+zn2TqI(eW*g0n+<>`kc_ktTgiGXRU=!J0-@}g2 z80h#S@osSZ3_D(8gS~(SfFfl6*d=@Yf4TF>@g)^Gm?onG zv|)_ZGlDWEsVHxii(XHkBfFd_Kttxx@m(ge3(NS@`ZRj6x`Ew=QQOQAEq!M zvA>P&&i5mW%ahppE)8giZ6F>O@uks;|ACSCGMtqiG4M0?2)@4$8KG$kdIHz=w1Xu z#@4U!lc}}4DfR|-zC4T)VcJg{+(X$+E<|@O@Qd+7{N`*C-G+b*YnlUd^hf?v&^qI-Kc7@5GuNV`Ya{s71?*5~2< zL`Sw}Solu=IeGyr=_U(;a3?^UAFN`_)5GZEFofK<8Q2{893zh$cMlQv28#$Cb{yQ3Eu1pN#y7Z==sI-*Js29mrncejO&<6c zYXe(cH~@T*jPe$H@O~;08|dA~h<~PbPMGN0425(BdX`L}cMNQPA)3$YUq^V$JILzj z4N4e?xc3p^4;l^b%OmWVaSR+yKQ;#x`wEeUuy+nI5^)AOr}OZErBQUJZyhBK3?qw& z;4I+)*~^#*Im$4yKR$%@t}G)fd!X2W#QAtXkdc%NbOpxo3jv z@fo-V`v=3wC8-zPJOoayZx-)aWJ9j7AL}0nl9_spZV>?|TOk3~HFh_?zlD-vPeR)s zMP{eT!0<0%bF_VAZM7G&k!$$P%`kQeYcq588d>UvTpw*6TR4VmKevo+%tKgj?Eu+= z%=ysp?p+T=->*-XQS!+gLIzOjx}SycrcUw2>ppxEat!?dFP<%v(7EwFSgoZ6G_d=*_0X^HIV5BuF zc1>9M&oE#~8^eEF+eWB8*Z9Eh6xufnCYh6M^k{&BPXL>}!dd}vg@~L%mVSxJge>G1 zM%e26+u{knwtIv+())( z00140V-pl2c5p_=7=Sks28O^1M8l`AW&v3Q3h*4l!+<>w!t;DNOoo>;6C?l-znkbs zNi4u!21fA_>KuANqQb6b3t_>o>=HQP3&JW&r_SQD5c}PuGykzNhOEqwV@t3dSQ(wi zwjg6qq=Ni{v4IZp#;~E$J#4f099>=6Mtcb~{D8%UYqN+?^pD^}dvoX|uyO}OL}Y1! zhOrh;(Q*3rUGMQK+RI!-sRR~2a@K44qioOh7SIBv=5(N<|3ra9=wM=gfRhR z9RawD*H1?;=@gWHGL27yMrM8c8gkPk_}UQ!!oeAHakY(*rV03P&lY;TJ%Y?k&tZFg z9DM2uP>7K&gmw(9{mK|Rb993D4}i#G<=|hiWf>!GU`svA_}0w|Ky8o(9%rJL)B}8c zY#9<->&V8yF1EY|d<~V3(H6+aLJtT!7IzRbi;Z6NZlFus7k{PRpjW_(?K5b9Js89G zSah5Ya)rK=jsNaKs%MOaj{yit8wL7$=NL2_tLO|QhtCeK@B`X1N`YVf5Wt%))-@!r ziTDTvZ_hW+;n9Jp@BSISu*n6OoQcwaYo8xFxa(c$2jFEGn;L>ImkYbt_R=iAJ-m)x z(vPrHpwcLPY;=k=b2kERhUuA8Y;LU|+Xi1jFC?dl@KuF#&BO){#<77bGRmUPqZhEv z=v&>!$M(s{<^>D8S*G3*c(5!Fjv(CQRpjP$6j_3FCFzQR^SCEq0T{tYdd^_W!oen} z6pTK5`o}ha2S;4wluSa_`VJ7{+%8ThOk*_GF1j_#LvP^jxELj(V>inpXRenJQ05;)tDu~lq({1jj2P2$IJ2VPOmVA0*Z8)u(`MBo&i zBF{kHkoC8V%l}zD0>Q))dUiws9Qg*>Wv*j%;tqPrJpX6U3U&n6nLS|GH<OGl5-n0F&*13;bgurb#7-9#TGb9*k3nd2kCyN1yb?#aKm z*!bDd2t=QI;B_*H%oD~@%G4No#iak0M?j`2fE+LMLA(4swgBI6_Vyq-KX!>r61oHo zEsNKWF)8r>0UC1-`{jA~^amju**iFmY%VMUU$Kbt_TiO!IfsufvVi^qo_~4_Uz)hM znrFW`$Ji!uZl`~o@*SOH^p8DBl2VI!Lqp+n;=fS-9dX^(?$%+RpOkp+}-((~_wNmvQtDh%?_^Rpv-?s6ZWV{PG!{Q%%B z_8{BvHUEqR%CtTTwz#s098U~m>~RLD*s<$1^4<9GJi;YY?*_&$(8J9MWSe<{91d>a zweRNpZ+y|H~aIS5j(0RUQWCWry&c=~Z-uu!avE zP_di+VT3fb^`DC!v=4S47l1JDkwInw_c~*H7~sJ}d~yRQkP9#@?arcyY*@nJE6&m} zI5v(zDy#k`Pis}J$23968FOKJ%2j10Z6RUO-r=A)1r8Wer6 z&0Q%zRv_IVUdvfO+3$X4qOqlcRII36kyN8|oALdI1POtZdbidj;p$R(GoL8cvi63^ z2oXo2dj_WWy_=gPq=J%#?M*_>^OfYfyIflxP2kFyWa6KjxlkJHP^chj(SwsEZMC~YXv)l?O+ z?Q~VtP?eU5NpAP{XppvW7l@9qtJmZAFqQXp%Wn_8mt&e>5^5+f6yl{CQ(<+_E}uWW z+oa{5bfmMWmxe-Vk(6&?oNcVVY@>CngRx&sYqf=4xvhLzt)z!hgk#e_#Lr|3#AD@AWXrW-BXkvY=qHwiscdTx#PN=s> zSchv>p^-|Vxv6MXl5>-_O^%dFYFozmH=6jw|Dwz$Y!c`g>!SQ)XZkp|>_O6OG8cp%~lHoEfIVCA(&bG0xrgDz? z5~a>QVip#8d7%cncFN(d?onFa=@MSHRw*HozF{$u_Dun4UK;8G+UgaRl|qrNf$@6j zNdeOL67!7>`5pWO^pcWdYrWc=x`oVL(u3>0b+Wa(b(>KcHSAVVF|D^$R;-ou(iDu=_Yg@` zYfrC|PI1(}XI)vNujC)CTdJ;@;;Pk><=J#kM@OT~D>=qH*r3UYPhQ;AEL~fc&#u6t zzDu;B%gHi5$k!^>Jh9Qyq`9%hM?ck9IiaOPu0YSSO4OzzR7uhzR3kN7!CXk$^nOQx zcv(@7gNs>Ur&_dZbZ&cljZRE}y_b4&VU$p*n3B6-yGp&kh>>V+vu0Cwu&{H2WmWh6 zV8=M4^a5}HsJOQK#;U#vMRLg!;@at%rdp8=@`ismxSX{PpemTeY3W&wQN{`^Lr z?)HTZT7Iq~4x+JUWf8^x=B|c(1uBiXQLgnFW}=B9Dp7%*N#bQ)X_g(`b&|R<(XCQB z?fSYEnjHlRHXYG^O+K~Z7M?B|EzZUY>iRh`4)w;mdNJ*_HL{g?9eTmxR`TJt0hNlC zmUTK}jm}|twbD8UaUMqb>5`_}88T7z;dWMb0WAV$(i#@gsj-0pIx!LzCQ8OG9lmj@ zD()4U>Wzg-W^UTi(vE7GO)ZW>iNV@I!iI{;O?iH8Dk^E-DH4U@ULKyNshNr~ncjuk zDut!>qW4YY$_*97ysgvBI!i)iObo08)GC@C<%@Ft#S^QVlER(Jb)teA6fG@t3_496 z6GfuC8pIRS{OeN!Vrz^wRV@=t4I1s->jV9>eWSH}q>}P&eYGM}gjD>E((IFy1M5qr z1*2V^#TtA=9U~?5Oqp)e6em?7EC9JcAXDe0Y} zY@Apv(;SlM)tp?Zmw zq-vh3aJWl`u5`P;eUn>tOH4;XfrYH5p{jGJk94(agNmU_rN2{sx`0=$cxHH~iW^_9 zvS_tbo~p8XVnm@{wpx9(VR}_lP(`q6MN2`vokOIYagkh-q+$9_4PueBuUC_Z{P;_`Bk) zb!$Z>l$r{fYpi1mlXBv8B*d+B-QDYbq?E+&-BSM;eiYL?WJ)^<^~i7xZV3W)Jb^^Hk0Ry7P!36#hT?KH8?uGZAH z4sZ02HdW7zb1YT2HI>N$KXg)bq-C>RTs41ySY2#jnu4gVbB&&(d6KI`gjA)a%6;ih z3qLQvI+dC>_ku*v27z>ESsC5-T2(i@K(9ve$Sz%1K1HQMLthQPwtMNdn)%J0f)&zD z5EEoDv}^67Vl5i;X&>Zf~{><*E?-&A?>-=6|wC}t(C%F zQjKN$%~tZ#dL?QOn#Mw*ow06>k_|5TdHg|H&HCwaStT9L5pE`Gt?AuP6%wHVAt8RE z7RlZsd|h3cpn>KSt&i(A3HOj`GB%4<(_Ee(8)qdVWUubsm7XG1q-kwbt?M3~8JKAx z)XkSBDO}_0&YuwxVeIZ#$Cs*CujTHgq2;MBXm4Ipkm97D1w#G?Ni%nc3WG3zpB%By z1dDJ!0VlDB?ySJp1iLI*pA?y#O5tjyhRQOZbb)SllNK3%`QUm74GC+zK$n!v=4jDS zr6e&);RFY(h=%rBCI3ca)y#$n88_?F*oIck@+x)5Hii6L`4YG!1HWbdwRQx9jLgY{}}%?oMy37w%B^uE`Tik}Kl# z6t!~kHMGECjl_}^bwyKXainFwgFfLCPH*$^kkuI)Mg~jc zoz(5t++OZgDBdL!%-1R7U0~MbVOD1=tEiUg=}@KCsh8qq=c3%=VLgGgV54Oo*_CN)=;x>35}XtelgU>d-dy5U#^+#LWM{2Ylol=G9@ zF zmG~=MwIb9VBI_MvTIwpIA_bb{#k7@j>S8?k9ej#J3_BXib7blR4D|SXtKD6SvveJ; z3LLsa1KM?BW2%hB({fGy^EB;3Vw^AWO9c0R#q~dA2!C zAivVw6jY)aD5B-0;-l{%<1bm>l$l_rYGumr$QO9eKqy|d)=s1@+fY-!$X3(NUpP+4 z)y2&t%uYr(Dc;q)J-9)*Q&!s9LdZr!Ldd;Y(Luhe(mmNAKvYWBCfKyf+TKUhyX#(y zXGNR4khM-(W_5<6UW>F-lCNS-kVJ-Sp;T6cQ?`>!PH<(KdRb|gdvdU?cyd^`ZG)~z zdv|rWS(CbQaB`eSgP&%iL}__uheUB@Ghb+9w|<>Ti)U<1Lr#cOkyd*`8ed(vl18^%aa>nmw*+6Uy-%vPN-Dpxg-~mL zV`P4#xQeNs;N$Nt7}d4DnUe6}Q#)bWU&y z^hh&LbB=J86YTKxG>i$+ODr`MNeu0(Xs-=WceD$SsZa403N{MJ38{86bIDC9Q4X-m zP)u-(D?hX;kF4NDGi)--|lPDBVuvIrPD5_8PEzT*iQR8#VNz77h zYL2K8%ruIYF0|3C&B?VY@^G~1_G-A7<{A;`9HLfhZXBks>M80hW?5+J=Ux=)?B|%5 zW8tLE$M{P+RZw?M}!y?}-8jG zum#x56DrEO?8R@67r^Gdj-8&ek&U^2j6S-C&Go|6aC;cJI;0~vBods610y@W3{)u$ zd{?M|q*D+|KMx(D-XNTvew;jah%Uo9CVQ2PtXy-k(M_0M!oX?p8bT=pa2$Cuh;2^7 zG3sUy#scRHdvprF-XHzT_$jslt`geTI&v{PjrI0&kPQkQ9hn2gJezeZXn%U^7?4_$C4Dn!wYJa!4o_7{@gN7vF;8R^lA@ZfG2a1f$@3=70+VY|BTJ z>p1&(3EkS60dd35AE4JdpQa!@Fyn56@oVdni*k1+;kAvsK;AQxLR{|gg>?QJ@`2L4LU%p3sH-2W12$mPfc z%DJ9F38(!yZ67H74bXe8FJg<#8y!10Q7XBtoOZs1szjgFSQD^!z5q9o)v3MtJDZ&^3CxFpiw;_M*Vp zgWn5$f5Z)h0VV{>1pTknK6L&FrW=e4^a%dZ5y}io12e*)68g0vaLj=xE-q)*qKJYuN;S`pf~c z%AUan;0_xlp1}-a1|L`$LvDuG|HWFwPB*#e{Px--0!p3RMM<0uoDPz{p7SC6xUU~f zgF`59nsnC#K9JRw3AAT;`mS#uCINHv2=8%F`?PX&mkZzBzE~Dogr`RaK;fII2*vR=c$o@bWOx^;W%=8_B zL&pgR;P*e@paH9H0Yq+zsY-~gW5^HWQIp9TB;*dVgSK7+;tFw|4~@t*0$yZ*~Q{P>)R zY}|}v+qAv^*&V~F#C@2YaM4Q$x-bYFWNmo`WvtI(^YbKZ0GttPhZ_iU9Uj87ZJfwq z9M z!7+L`Nk%9OGvHL9q2z%hWPNe?uOk~+-}D7W0k6l-=`yx5b@I={-n$_Z4Mvf}=+z83 zJ$BZxJrWI~RDW7)mhe+Lh|7-|D0`oPZ4n5N5uEr3eH~lh8pIjgew0cZ!)Dl%=rMg1 zyh2oL76LdMG?3?WPH+~r=O3e25FePkn;PDPvwJ3JXs*FD3$B~fGZ3^uqsHtcI(5oM zkC+$e4yX)9;BB$P1if7kJeCVH$OX)tF4^GGUS7cnXP^oK-RlC_jnC&v=oxhYWnFJ# z)BSxgeqq2|9CSBF%V7In#b}^6T_^4T^^k=h?lSL2IsF(3T;iJxJbZ_7@qbxFWNV*{ zb0L$p$^*wYxbN9#1e|jTKK!9EbcsF$Ql=S*xiJ1hg+$IM4d1>QM>sI)BhOy^-`-0| zSivNYJC0s(PBCiFDU?8rKs=HOeu^#RdVK@kU!-H_tG)OXCOH*ALs{@clEAq~hqYktU=1P7jN;pqXK)ry1!?^a zoXmswXPSl%!m3LptsuKcFlS*ydW^k>AHfS`W|)rgU`j?9UjcU|SgJ+`5OVanO1xW| zuTOZhoUDX-U4>|Pw})bQR+>&*jgCTioN%nKX@XFWns$YMgQm4|riqJ}xLk;DV`93e zgH>mup+c*sO>%>Ag1T+0ousHuQ9x+EyNsSnaA25bRF$leSz55Wd7+$}n|XM=QJ`Cg zS8$iJv4&`pnUFxKp1F5^V0B$Uo_K(%5_@+iRIeo`C zG3^lZxULuhHGVPiiU!4ey{;UAWb67i@k9q@NiUzo=3x7(cKvowMQz7I7o(_BmD;@6-$*4U7`EpDxSuYYU&QrIkj!-IYrLNX5D3VU3#{rsmkh}y2%dWYAv#j zlDYnZK_P*rErRvXYm_3JY^InIU?UYG9j|8WuhZ$>rkbW1rOB6()uPzW?^e*Em6Iu@ zo=~A#lc3gZ7n7oHWiOVIs*vO!C?}!Npk*D|6lx;l>1P|^&{!?pE#*^RrD&$v7$H<^ z6H=9-mEWXNuG?;D5g?pYFAyVT+fkrzED&9mXI*Y1)#%rn72lrL;V0f{;pwNRq>>$L zFK?{TsicyiEMHfbm+vYOW1nj0T-aSzWt-k9og~8#WPxp^gN<^ojAK@6XHcuGh(WiL zsj@+)avP+bQ>}D8(u3UO>;=n3ecS@#!zI*&jHR8#CBsblMrb>%wtBFfCxYm@&g=)q+3rRPm z7G(+BRK!SY+snmd#6^{8$e5W4Cs)gR7in3U28HkoxwPGvY>}w6itO}PFKD!`kn?d@ zYL94Fi8Jn2_KprvR7;6#;Li>d?sjRBtcpzum-3I0sR;90kqnV9R&9%maaZzEDbgw^ z@U&2`NsJK{x#tGk_(G|;EOikpqb?0;#X4m(@6Ld1Cj%vaF|TxWZPDsbzm95aQTadt zfm~(zXmM|?W;+$@Y(2YDJR26->>gh)@tKCcq6y6fKjo`KU;FsoodzcN8czv}&q|QWWSc z5NMZ%ZnOkH#V(taDrqr?D(4O<|Edbr7|S+sucUn6)(-ImMY-^z6m6%D5>=&)N-y=W zNGErLO84;Y`ug@J-FmeoJ(VPZIwO%5=}>KZ(foG(tSFcCcn4=!4|%By`B-m#TVWUT z@~Ct%m+mrcu{zzzvcOia)L47LV7(4Ls|c|+c|JD*B?TuF_XxX`?tr@ch02Wpctl&; z*A#VS>!!7c`L!BL+;fXFD6z0hanmv=)3lclsZFmbaELaGYt-?Mt1E4H&kPdic9qdI zmAUWH9F&!n<}R;QXsN`1Uqn4rBv>p^O+75Jp**&*u|hyXPg6F=S1lmcr#L2CFd;7Q zUVg2$w~TjKg@03km|CH7rhJ5MXQ4|@QKx2%m4{`ONO^ioL#MM(iL#||X<@yMOqhkP znOK{@R%QvGqkuxDfq}4Yr?5(+Y?O86eLW-DWE<($1Zh5dzMy8?()f(3Ojo)~OF@TXD-2M?Wt6f?oSHqH@)I=+ z;zL9#%T=w~4KlJSllh9I6cWtp6LJo1GKYzu89rR0muJM~1=0^0Qjq`Gr$)pVOH z(n@s8%sq4DvT7jIoJEeyPCbJF?bCCZ8d z)M{EXBFn;^Leli~6x|(ts*9yNi+~3VOm?n~%JQ@;^o(+-s_(QEFpsIp$!b&0HcSvu zOw4F5??`imR?MWt1XC3$ojARW(17gZHnn{FBz-?&F$b;uRO#R*(KeaX)_XFgT_P%N zc@dp;(*F0d)pWdzLbTK63jD2WYpqqfA{0|RTcc3gqLmElY#lqka60 zTT2Q=)I}`QT0(2P3j@^C6{F>|3QUp(OMFzc6Jjleiv-$=4SjQ3;tT5~w=iuLt<&5{jFm69V08njZP>*P%o#KNt^)N5S)WlfYM z{1jv|!=l?;&0_8qD0mu3m3zwarHi=AIEp&tmuuufZKJJ_iK#~!e}(G(To+HZfW)w< z0u%Ehft<#U;H)lNQBgZ@Psg14l9EU%k1lV&g1C-*Azj^>!V%FiZPiM-b-}3(742P3 zW)3m-m2K*68NzCX!7gDQ;$F1^!n!HNVX`r8b^iInA<9x6DoOg1D#;z0CejA7?ZU++ zd4Y-5QpFmbNrhQW6bFU@q4(6W`!vd-}qjZ0E*wWv(;YV}i1PfF`Dl4z5aj1jE& z&+{@i%de`ijIfJpE>mhYznX|K1G*D*EFZVZ=d zv@X17@7LjBXu>ZT-K?V9P~H#`Am^U!2)&^pIaXroS&eGJO-=6gvWc!aA;Pf@6^<_L zKAEZErdGNI85JSnX6B7$D#Bv?d11Z{@UfkMb)aRijv=7X5 z%Ps7-kCJwA$d{DK>eiDhEiDUDHZJONPRsX%8#%n$&yrtOM71#`N+V7})+MG^Ln)?B ztHv$z+K9i&p|~_E z!YQx0vc%d|F(}cd%GqD4!9qX1aiYPSfX$WGmCOX>J}lg?;C z@B5jq4)SGzUID=^q3LyYz8Q8Q2C7a$W|o4PUh*~Jov~$^y6K`>j&8Y$l9h6Wxw=OD zH8qj?=_bxn_nXqi?Mh-is=7SF`OHMa<@sF`?{_)*c{WSfAwXIN$txJiGD)Bc~FDVo-3v03xPe_!PQuhnVRx*fCaVo6p^75%`sgJ0P z2(#BUfoG}7q06tVT~jmEEIp(tu-)pOo3o=zicE}eyOD5`g}+p#M0J>PM~10iNRe2S zoo1U_fs1*EO-Of!qm_%Mbf$`(k!4_{L9keTibZW$QF?KelX8j7y{5o0QFC>3`wo}z zsz5Pzhug6j;!^b-u>gyxtB8`k?NSmmdbhZ#tTxtc0hW$4`x zj|?l72}*IzQ!43l2rg`P)N2;3@O4iVikB?O^T6vW zv`>x_vds2OG>UfD@>Uk_P}41O>k_gsY|ifr*RlysS26Ob7f6b8k5a0<*Y2OE;a6l_ zp3>#6B4%x)r74oA<>PNE-z}=G<7Ln!q*rMqohNN*XUpFuEEHOw5*U}$;ais3#;>a< z?PQcBsn!*8zpW@y-A>(3*}b{ls`E5qVo$fh$e&TL|?oNfJ`u2ii@ln?GPQLD;O17bf?n-VgfsP{lnWg2XNd*c< zrS_GoUa@T~fmHc6Fg zOqNM7mugolwKck*q}@=EV`U$sYmivz5F4k=*PtBBSD#_tpqpNz7i9ojViB(p^-|CF zfNGI&8QXA0Edc>FEpNfFOf%WmG9AC%Vr}EhQrUJzDJ3tX&N4+~m5>T+wcx5imC&dZ zQGuXH+lu>TcKJ$Go~GX6`4u)6QbGX&4jPU3DjUQE^6IrEM9Z9f#AHkaouks*>IBl{ zx+~g*Z0e;08xyq5tTUh}yEH=CKHW>Uwk;vADXSsWu*SqPP*^)f(z8A(N;yT;kzg2*PgE?0crw-K|uf;GiX~X%nIhGU~}G;@#*xFg@36C}R_9Gw4+Oa*TpsLb=(( z>>A1(W&FRDg}-cVVKc)!=m^v%ZGeE72+8m1>rsq&e1jYkp;{5#-Q+{o9f1ID=)+Ou zY8oIhGUyXGxY!D77@K4aqC>1{gtUGNQR8`hlLp+vK0s@mfJ`z+k!u3jS=amUp6xAw z$=K-b>LNxRVpd$bLjaS#)g@f0J(7g z$${hEvn=`oBdcERxt!v@_%~SCdK@RKSj&MATpPT?tHocD=o$=5CNUw58yt}b0 z3eMtA<1_2$e_=v3CopYH<^eiM*h4A1=l^7YpnY%hFF>SS5ti`<7=Rtk9brS~mw#F7 z!v^4p_=0qWPEH-8bTU-gaG_R+bp>$U1UAmxKzkTWghv^}HaO>h+n>Nzp|XIwev0-( zuNe6Nl+OFx_+&58C8Q;N;gcf=e}{YR z06?DW9_Uz{oxlm4ng8xgLSO4V%<+ye(8Hn|yxzYJFM&*b8lUZfb_Uuw&Rso%n!8yH z?rEIOoJA=B?#{CJkomU5?o`ZAf z!9V)R_`%6AvN^!Tj+j76+|baz3+M$PoMD5@=h*TMfR>aIIJdp{gU-Zesngi($s$Y! zss9}x!PmEr@UulIu{>OWoIT*=`x6Lzd=WaEkMD+i&M?jd5#Ja-#^8J$odtz12jbQ1 zy?fXV@$}!I#@-mDA$y4nFLCTYP&-J1%7y9kb!-Zn_y%?-kgc^7V0NI9 zVxNVR_m}aNlLKfG-9Tt7QwW>MK#2gXvle=xO@xFnAp1Pcg{IL}m}0GfDjX_vX9ni6 zlli$nILH4>AHbK_&T$}-{oLhu?rX>%#W-i=kVCf z?;N4m+sEJtfJbQ&jK`Bx5Y^vDcMqrt;|v1wf7;_oENq<(yxSO5`pqui4YMi#8eBoA z#wZwhZ4=q+oxU5GhXR{H=!m4BL%Zt&dP66}x&T)6gHwzH#bl&YxD7Apz*~SAc4Fi| z8$);fbf|(x0i6j{sIh4f$*9?x*M4TIA9g#Ay+Wi*x1A_)`qbg8VP6b58_AA;WT^<_u=IV zemccO)=vR`nuLK9aSoqYC84bS9efig!1;qYgg8$^7D@1ALPy@*#w36cgV@F8*4-$b z1RV!}xXcouJ7xe~=0W2BU=&J=m+pqnNcij~4<8-4K?j#%T(dHXt_}d!LRf^RPZ~O~ z*auA{rwEmPgZ2)P@N+1`m|VHW*B03*gL8H_cmm}EvnNpPzWMLf841^#|(1zW04! zhTjjhKHK=U^zi88@wdUpq~Fe8&b(c{UHmot>FVqC=e{4;AC7*V{36~izuEn5@D1xX z@*~EJ{@>@{tvsMVUVOd(ZQz#rc;I&qh)vf&^gd+&zW(|8@#^cb51dcqZyCRHUoX9x zc{TUu%KgpGkCE3);NB#CW;r}N|F8k#%AVIV4@V!b-7+4Ge_r`@^p5a}_?qy6`+Vu; z{Ev;Nt6xUpZ(sO8d^4T$aO?F>)^BT%CU2=92(Q&o|w zFUv|VX^)p5@xC%Xt@z15BECL(zwmMH)A;A%m*Y<;kG8&DzCL<=^=$Ih;Mc{U+$U>4 zC!Z}mpZ(SMl=*G-3+wUB_nya-PsmS(U-$pKcuV>*`iS-7@ZHI`-p`w#=ih;c^xe`U z*7xgoyVCEdKl(qjKhJzSd35~r^6~JencIPnQ?F)!9Dllcvhe-rmimD4jPMJrmAAt` z`X4iYJO0)8hW>cz!S$!f*IOVSU2=NA^oIF-{o%mlh0m*H@6KKyJ??w7`jM6NhWBOV z731ydPxAMjCzQvFFLs}=ydQtY{U*>^+F4t|?{I`V7xEZ<|khfA4!r z`bPhD@@4Y*!b8GO?uVftgtr%umfjCNVn3h%efWLi z=T}cwK9Jup{Th5T_MZ0}t@|DQ?dC1_&E=!BZ-c*fK5>2<_WZu|j`foLYUq~o;OzVQ z)3qN{U&emXfAT(1J{*7Udjh}ei;G_ypU>Z%{XT!Y@MQAs-Y4GghYwf2uRq)V$bQkE z_?`Ue=oR_r@Yk8w$6yZad*Ac<>dnlL@rTq0SNC6?-10tgzHf=Xn0nmz_~gs=i-q@x z@Ug7ij{HvhxbcON@oVEX+3VBgkBJwnUwa>(y*Yl*dryC`{*3(f^gaDK@%H4$^moG3 zwU4~7R}aZC>OXfMtUOrzaP*P(gY)*L@;Bns-H+Sv$q&chpM72bu>6VlhWCr{p7L_+ z)5Uw{yMgDck4G|pfd>2b=G*Rr`H!^U1|G6*C!WsUj=tbL;Ql1NK7UDi$#{EuOZ~F_ z{N^F!!P!IVr~dDRkMwtpFJn*GpE>U-?>FA>d}DuRKIr+d^XB?@+L!*fYmb;Ora#Yp z9DYpux%H0l;?(*3{_WxI&a3%PjCag0n@@+{ZK?m{Jh*zf|8D&K!H1o1{XgJh{@(kd z|MvX*>63|Hq%Xv;6AwAxiBG3r&wpL{9fYRuiSHL*pZsFJSb4Vma^&g78|EkYiGJI7 z0)NLF^6Rm$eGj?M`vSgGzwutq8ooS~f5v^veYCIs{P6MmQ_82!hwJYbz8`+ydqn)O z_IdNS)yHFRNM)~gpY**LeKz;%DVb7bJuk;^FPxc2e)ItA3vRcJNBZ#F#d!vuopZY)DJRW(^ z`95I?hP+%4nP zk#I^XJFl)}}_k8Txqq+xGLL$IM4PZ?BYIQeJPq?fH80oce_K?c{yWC(@haS3B<} zKVCmz!AHt?dH9P^_IX^(p%+_t*9L|d-~Pbhe+K68my?fYzYg5e-}QbZ z|C)Nc_GaPfAefj>e#}0({?Kzf@Y~V@=Fh>W$3Ny@PQG0EwD+;^_4vpAw~OyiZ-?IQ z{b|gydga7eLnnd@xl6ov*(AuE`Qs3NPao?e(C4o+y0+S zFq|(xn0__$ao{!Y;rhc#yWh6oE_^3{nSa85%erMfA--e2S^ItD*})_B?Zxw%hukN_ zFL;kQw|kG*e{Fpmd_U;$l=*J?3FXDm$DP}w=lwr=KCXUPe9e10_2A~W&G*+IXFoDN zZ@$|7IQ?_;2lYMs5$)Bv@9pyUkyoQ%2#+|Q#$K#_S$RRfUHWnJknrjJ8SfG4nF3{`yMYoAAH69adrFuadg)2aaHTzF4EFNOL0F?4h4#oQp&eDrKM2xK#_yf;^hFv zt-yhCcXxMpm+Oj5GMUI^CO&bQWG0hjOa6fO>HBLXGueBuwVt*1^SN*0RJN=wvqxo< z$ttI6?y?T6`Xse@MO-3G=U$o-c1kOm3T2GZlb#^91Le9i+9ezflm&(8S3~J5d#zoX zN@z^im7k&Y@n8)ij;e~5s=2}+v`l(_?6Qrxhr?6O4p+aW!t8d>c&CD6>Q{AN`;Xn_geSRwQUdw4ybwsfDpMlQr>=M5l?4|%JD!!*B=|Pw;LX+0Xe(m zd8pggWu0V<3c4JhfZ0G-)*kIPO_do+J#Us)inKg*_@5&`(PD^l^7ix+bX=;vnUlqIiNh zPJ{0gouqe)hbW!giDVnFtgBwQ*q*NRsCZgZi%gh1Lc98j9kxOFguMXeO_4fI=@pF$ zOOA5-fi~jdK4Z<$t{x8b+uA*2jEZ|SJ>xHMUK!W)82U)?I|jOFJ;Y&02eared*A8P zw?!r$75`nM`hMOhq1V?<9HsSV+kI1!H@YYX$Cb&Q*=zLMtB{%Df=LIMqbrbnca$VazNc<81zp%I~arRvac;X5Srw5Yda%7qB6Va ztTL;jo=nLzMHtnM>N_N(tU|It@8=F_it5h%fMYsX@V3WJ73Ce&kwk|Q@bu0~ew_1F z!BUhLBZJ9epvdU3R+Igya;7(2)3jv@$$|8Z7xZ%{L<7{ZP;V6I>Y;+D%hn(1j}?qV z!k*NaWe^6zsCUZTYw0FcNk?W(Gps&EfeKX?x!VGTuYWvRl$Qx5(rBPlylYfcjCTqK z1mm7z$&jyK4W@+DM7YB?86J;a-Q}HfcAJZes&*tj;vEwW>I37d?`UQQyQOb;R$T>tDVE1N<(g{*e zyy%+nO`1mIeWa3LBs~}}n895Tui7UgovImfhj=>NZR&=iYerQix2vYfBas>YRJvb! zwvRHbE%Li%W3FCXr)%8Pmz&P^h6jT^qPAqWV4Box?sS!i<#@ZIM4Gk@^4neQlx|g9 zXuvzHn3hh7O2JGrmUqV{ndOmGa9NW1}S6F zvaUN0QwVdEJrgcQXT)Qc;e0t&<&DcLlo7>^1F3Bjt|4w0y_y+xOfkk8Gp12>hk8sp z!zd9;zMjBjti~Tn4l#x??7Y42fBncW=U=h=*;nq{deN3sPj5Z^+`b)K4_tiWySLui@$K(7 zJafqv*WJ10$KS5mvGc&ayKj5*(5Z(nKk&&bPyBh!i(B73cJs&g9DV(gAI|)G<6RG2 z_}$SDUp>3?+Y6q5^4l}_Z@F~ax4WJ={?h**yydr#PCR?twR@l0_0E=^-+Zv;r_1+T zb-|u()B@WKUuy>->6JOB0VJ%@if zap>iLo`3l*H^2YC+poQ9|84i5z5lrz4!&~Jmroqp`P|8~2j6&U`=1Z(-1hUEpYA*J z(CHWMK7GZbryl&^wI3hbb@sr$A3pi_br0Qk({rbueQnRTm)>(`=ii6kfAzA@pWS=@ z*6+68eba%rpT7Q!*U#K}_RIeryzu6Cul@L&?=ODtmqS-S_Q$W!ZaMppH-Gwa`)5ym zaP==A?tk~>KknRh>&rjC`ts+W-*Me#*Pi##1<(Bb$JsZoIr8L#r~dKz2fv=&w)Oaz z|9A6!?>uz;^t)eO@x=3getG8fmjCV9``6KTKY3`+Z~u3A-|f#obM3EpUVqQ2kAJxQ zDd;4*?S_jFKYQ;(|N3;>^H=}*-+kYlf5Tm`?D+Y!3$|YM*^x{C@$i3NefZ{Gr(XN^ zj*q_H_T$-|H*Ej%*qx_eKK9ScuKwSD-#xhPhyOgZ^ZrY=+;j2UZ@jYo@9&@g`(M9Z z_m5Y9{`c31uX^H(m)?Hx`NL0Kb>YJ&9=YR-L$5vk!drK4zwq+scU^tYu^VsM{_DHf zUH;Lbx31iN)ro^gpMB+%tFAcw;96?s)vvXS;7a`pzA%U-R9Ihu%2<@h=}ab>{o)5B_)mHxJ)& z=X<}u|L!%HU2(^ryYD*hy6tygc+K9c?|JC=XYW7u!Cxny{oi}X?>zFz!JmKq?7_Rf zeek2pU;X8#fA6{C%y)l2{`%F|?EUkyM^69v{lRD6`*r`VyAK^c^~1flZ~Nloqkr#v z@3y`F{^sOYUvGQiu0I~X{F~d3AH3k6FK)l%!=tb7xaF4J?|u58pKspr)PMf(_;*`& zAK0_?vQM{O{L%K4yLMgk&OOikwEM;f9{=LN%b(rw$M+vR{qjp+UjFxsSDm`$w^z

pr^e@^6m+`thr`A9(Gf`yaS!$CvLsd&R@Q|8o4i#~%9e>g$edzxVA!H(&YS?VsKG z-{0>%^yWnu?fK=8^Dnsjv4{RVvhUIp*PgiQ%nLuBf6rh4_si!`TzKspSDd=#)Uj_4 zp8VmigV&#a>EheY|MtL%UDw?H@vEoae)qust6#kAo}=3j?LPX#zMnt6==ztQ`r+iR zf9=`+;KM&}d-~)jdynmUeEXqC@4Ii$W3PPj+C?}1e)aD!KX&YbKaRZh>#L{t-2BK_ z#}B^$`#=Bn>?POVaLI{_pFFVj*p+wO_~*m_+5glNKRj^h_3s=$@WQQsynf@3=RbP( zhCg4t?dn%QIeppt$FACc-;+0fyl?yI6GwM#fBgS;zzgWlUO0XCTNk`_=$l<4v{wyzTeb z4&QTZ?-l>u|HcnDy|Cr|FZR5A&BO2A{NSl)E`IFGf1N)1`@yg8-f`KP-!3_O?Xjy~ zKYi)Te?I&Eq3f@`?(rwy-?4k=j)OP-_}R;EJa*#vJv#g&u`!P z-)%P^|KPg&cJ6!S-uv$S^V_Y5o_YP>um67ZkE<@Y>Cy}TbN2jwcYlBMz5m|({_ZEf z+jIV_AAI@9k;8j${PL1l-@5zQyJs$a@$V!1U;p~+NB;Zs;eY;l_Rx(duf6c+AHUuC z)VIek`QgItZ~cDWz5o94z$f?qeD=aWKmNxL*FN>tw!QCO|I=rXmcMTM!P}mE?WyNK zKlb?LXHI{x^@0=M-1*|^n@_y(=G`ysylT%S+ulC=I`pYru=TBnKK$yHZ}0x>(ft=6 zdGXLUJNEB*=n{CVeZ`)K_IPx?P@WbC<+Wpvp zhi|{|p)0qXJo@?J-yit-i7Wqk@;Vk6gLu`!hFO|I5Rt&wTj#AD4c4^xu2;?tkynL)$LefBL=yZ=X2w$(^@;{*O;z zzW3=L_wG9N%geX^Obx~bm^H(p1%LcGly@#?ABNIZn^cA$G&*( z%@dcO|G=19 zFr`;Ey2QRLV@Bj9agI7HE^36AY^uXsb zETGI_JeD6qMrk;gc41_l6xC(4koyrMj<^qz6B(NnV93~&CRE85AreOdO8`_;0PMkN zn1+SoY#?Z|aEU3rN)O=zxdt~eRY=;V!3^>!>I7;HMGJu?winUBi>R0o40S;z;!Ofs z%c^S-f~Xe0frXR@qa*MeU15|X65$#a7zLdPln-o770-)g^?KaP2l^p}k8(u}_?SG( z^apVPLx);K!cDd-jeAX@O_me9>1-QDfa7qn5h%YbV0<}z;G5-QewGVyna*+Q0Eq@H zS^?LL#2o;&qEJ^-7EBGV<6`MFO7?~@uOL2QLPWqqqjB?Cj1U90y$JPy!PIPKVo8Pp z@#jD}s)D!({A(LAjfTky)Td9NTAl^Sk3^hf_8{_%1J^KVNW=&vFlPX_@tBx0!9ZeU z2zNjtjs?a<0SVPusfZd`kH;-iGLiXh567MAVhB*-IkcY%h>w~yt_6X! zUJyG6Sw^EdH886LV8RrGEEJ$*5}}Sg>5ih>6T}#22(6K874|bHQ#8OydY&gEL9M-%RCD zpUMUvOU))Po5EdD1IFZ~QDa7cCdg?B$9UJ3CR_~oH=hpP;_)*$SIEP?K%{d3#oD8j zK+H~tGKDEzYLG0;>{uMW5#Q;+0#YZEjzu>~3CMPE9GDI0zmd2NSdJzvCJJqmSYkYp zBq5G4(0hpycw?+TmpH@2bPnd1fb`d-hpdeVQ_uk2Z3JQ{RRnKZAo&8W$b2Y(NRwhD z!d78=w+GxzF<}g<2iKTQ z7|{yiWvdy{**U052cT+#vK3_EW{~=_lS*8bk^qF5f$L=s#2L5aau&Qz69-T_gNqql zIg~`xVhpwm6+{Vu^QGe~;kmI$L{d@#Y5+`R4B(g%T>y)QEWmU2;@PlgT@560YZ$cz zL%7fj#%VFUKL-n@6l}3kKMhhsN}R`mBo4fcHG_-TFLD6_%Z?IqN+jr!pkkL0H8JQo z%^t?RAXtwFsLNpuN`#CP!AwAXba9;nkUtId7_eBVlWoJbLMI2Hq(X-o7+PE-^gt#EIv?mxl+IHkzBq4_$p~z+LQ&M1 z4qytA2s4Jcb215*Gs~8(YRnn)%T#z)=#B`w4g?=|_^5@QO{3x{@qhM*+_(Kq8fNCa#UE*OY0@Npv82 zUJ?&RQkcv|16Z61rXe7_dL2kw0(@^l0QX547~4WShiXML-YBBXvXF=_gC&HtO&%Nm zj?6*n!8qD5hk^e9ZSfpn^FYVR#b`fYV$`faOVI)NYn-5e`4X#rIw^q#F zB$F7JC9cM0G7sVwMiB+TR3jWa=7#sJs(>1k+MUq#6GPN6Q}EMdTotEcA-4o|N9Cx5 z%fuW4=!{@6Fc}#Px7-|_bs7;naBvwwk2MQPh!zG86Ab3zuno(_O^6yeXjYpCLVS5l z?q^_PT^g7A+-OJ>K(cBGe98DoI1Ye2Ndgdt22>(9qEW67=Z2&h4_xjUGXoYi4IWjp zP_>V+z%b+Pm;@uJh(Iz7A{t^A-qTVM56^@I7*r%3ge)e*50vvPrUis~(LLmN)V-6Yt*Vv%#C(K0iA_`(r#!w%@iIMnToTO4> z`ExJ#oi3DT1S~LwoKqeSl7<<8%8u6=@l`Q2Tv(u|2%v!;A`7R9U8s!@mn9CQOCJlT zT6l;iLPdo79PZWGQGrs3$ea+fOj1@uGA!@3;bA3rlgNb)2}fhNAS-}b*@kD+AnNuKu#6U{ z)Ic5AgL|24R-$HOYLg~&qd}4$cPo8Z7Lu_tk{^Q6YQ&}rqC}wDifA-U77n99I{7)Ue;&#DZ1kStkv zytODNCBP*R_syQGf1qnZ56c?aPC-e544xx$5feua3|)v|TVgE-kO+M~44ecgIU3w# zawIq{$$)u^gr*aaC1&|CvRS;zl~S-ghl**9LDc6ZA>LRV^LU{Ti31!>uM`bLsi@e- zz>T6L9+rrqTv8%oqY!elG+gA#1O73L3#m+uL$hOfS9FtY1QK@Khl|bu53D*MDhoHs z8Nw#TAx2ri-!<5on3_UHqCp9e^>rv!Pr}In<5okPnZOTxRY49BWdoQq3%`~fV$10$ zmbIx6KdeGgngwOSs-qV(5sf#7xzn)9q-cmn$Uun}GHz3ukd!Hf22u))Z#6)}2@EK! z5!Orvq5^_1(Hum{Ks=C0t#}BU5Y<@;N@hWjIxK<7N{HlD8bn5kB1Wwn(PRu56*{dl zAlN2SC|HsWjde0Fa*i@#;j*v{8`{L!V#E~`ATAC8913C7&K00;b^8BP`{_bR344QR zES5$h5hdnQ8ZfI4aO-|4$hi}^Q(;3neg>WbPlt*K*W5zF0~7#DLKd8pH3Gst0T^W9 zeR@@!Bp)4fiM^Nw$lqp;3+5jlP)QZ2iIUu8Y777|=E3K~?+SVCRE`OV1^=7}{NZjV z5_G3GxfTty*nxzE653>{IGEO!05*jR$?3T*w;SvXz@)aPuq13pl1d7$O>hvsmJ9Lc z@FqRZ!9ofSB7*p{Mj*rk4xniQ&RzjIWfm-|e7O(L2QrX*mY^mL4@=9Ws9eQHbK&eJ zl^=sfJRYjgLq`ioifZK~M3I6b-D1OeA(-NXAW;N22*BHe}*PN6AVN;`d5h zk{qtEkd~EnT<+H+Dp3X}07W8TS0R=#0m=Fpn2!Z6hhzd(+a0)2LENNpxEpdPyriUM z4`~V7?c#YFPBhT41SkhWkvMoDa!67Hm}s$l-AY2Fc0J-!TX7=vTj`RpYH)}RWe_zG zT&Pol>-H!ja9;Z9{SbHsrY zxpHVMgV?((jEhXtO`3xN$bSu@$U{38F^Y(i96UzyA$kqy?jdt6=kswoRTjTa434EM@~zJyg6h8|F=l zaUB#UK}i6mu}p{uiZCxPfLTloBt?n94e+7FCx-^QN+oh;pCVb*Z54BIT}EeybR7` zJ8?#a4i*3z$O}Z!_65Ke_{s5L8g+_*Mi9%PY~bmpO$pr3G$5W34NC!$Swb-&&a5AE zF#{M$o!>AjQM+1&XJQPf0f;zDqJbHngxb;!)R)x5V@SXfG$2J=LTD~W#<^~|of$DA zNNI4jSpWnNSDoj&uqhl%nDqX;=p<@ zN98Oh>QFM4SPVQ6NMbo{0`+9n7&Dhgq}KGh*akRRIi3@7Hd$m~<)+zCB?IF>n8dR} z81xP^PGb^LQHp}8vvL^aNmRlk;u0X z`iZET#0FS3fUc!}+^lvY8t`W*%>kV5g_bzreyEfrET7_RG6fWr8FHeuI0ucx?Gb>0 zh(?NFR;y~0M#`-b&UqLTs5J?@CO|W=OtLje7!NSbSe|c2{5&0^XCzPo5gr{sYm?~r zZ8D%fRLcN5BGjNPSOy{#C#rA|FghcJhBYv~yZr#5iD6og5XsUsnA8%*=&*1Glh6yx zu%XJJ6EPb!NSp`&1*oHAKs$HDbC?Obnf+WUCiat7pytq|TsXjg@Nk5Q$n=h;l!FN* zCM+&CuJL@B1L_Bn7!b#>Rg3b4@VMwORXEd>0Nf)fM^ym@l>OpOs@jXFtWXLDESL?d z%$O<&whS_+PCM`nMGLwm0MYoADA^)|5g9@<*8C=&sKLc^c!~FRG?oTuEl!ysz3&0^K z05l_H!>#qfvL9;1fN|~cKq1UPqYh&G<<#wf{CB`&nsJbDrcjG>(kaBqp#ZvG4h@E4 z#OqCAEO8h%{YE?sgoBs}M#B4ULBcq>da6wu)jo);x& zg3DZh%;!>wkSRqyuw9i4$N-853`~FxlbCyxl7*rN;2s7)8#gdCxLhyUB>VhmFqJ}? zBo*fN#&9~2|C2U8qS2~1DO@&Y3-a)Qhl_eG0MSTNu%J(Y3mh3))ze2n zM8*uN<;Spi4$#*?mGM!9phN(Tl|_Wg*fLy8_iu6%#7&lghr|R1l%iL!aLlMXD92^I z49b>)$AKO~EVd9Hc2Jw3auBGXyi~Dpf!l_;#WUB-bP+ag0gjkI4#9S1ZL-au; zr=a6bJy7)hKGf-^A!0FYlWsPk&a42nkm9Hm;7obw4o_#)xIt}3Bypgvt2LO00<0cY z2)D7o3}%5AW_2FX!R|K-tR5%KjEqD8Hxh|JueV`LqjDiH!g*Y9?c~X@su~t~X+%VU z4Ix8^=R-7kXCSGc|<~IBf=;VHl709OgZOh19KSEWRge(_vd&hkxavMOzMUv zfipc}j1eUu5f=$h%W{ZMrEk(KIEQlXS)l=e--zi1bl}9m^8_a(Y9n!zmjeeA1JL#k zcU_>x5;_A)m%1%K`@sW2#*|terk4Z}xq%6XKMGVxggFHs zLIVp@7NE|enCP3N6ezLO1}M=|U`a5eT&O=BQ6`)&*f5$F;OZI|#)`w7VX+~uXcXsU zoTxD`fa4f7mKC~ihYUD490A6M^g&7?#n>9gJcEN0l57Bf$8oCKxsaA1dSwKUh;cW?7g7F35CS~AOdxY0O~Kg zqi$fMK`j~c8l@;nnZboI?$_%F;=Yae)Mjkb(0wd{CD`LpaQ%Y8pfzNCH#` z%q$kzUBVKWx5!b{0u`m->c`B145)@8xDJdcYLa4;EzZEcLWg8C30y7KBVi6CWnc&r z;8}=M#c-h+xEktgiz$pnXa=D6NAWZh2txEMnhT!ebosCtJQT?Y^ifCEuy5huvRDMQ z=Q5zo02>QjT@5XQt75R%u?PUA9%#@sh!743ogjcQH0U1bUefn0OU~)&_E1;skzh`Md*VM z3?dPU6ctM>Q1Khrf%+8W13pdxRbCYKV1y7FHh7RA8xmnsJ#Gh(5h-BD9da2a_IWS~ ze1Zr(9R`ja&8Tx&4o;N3UM?sDRfrLGB>IqPlM3^YGYr#vI)fQ#5MauBkKps7d{4dsG(Cd5X9ptMQHWN1*y#7%TASacu=VHbeDK!+r<4$L2i zfJY9pA^|Ro$FbL{FTs~hhPwl9Elr|f?G{YP2f^6jpT?@ zoRdr84#50Lj1b?@val>$v0w#KlRb<1j0D_baKQb8+DpR1DS%l56%pdoWH37^i+e-r zg>V3MrD(X7u3rk1(X1MzGE^b%N;|+(j#2Yz)asOBYL*0ZMC6Dnbli4|a8*dR=2!iBJ|PlopS-Ar>x$?Mr#%y4Tc_yB7G4 zxlm?B6K)1WYXZ=lla>g!^&D?7vCfLrz3x>3ZP6}Tj^Xi-VL6m7@OzX=RGj+#0?v7j%D7k zn-)lkRd%jHF*H-o4NGh>!dT8)7D@aS$djd*>t23~YhDRaRv7%I*|(;nHn{edKHkP# zip%D_-gRbRIpA*yLiJ>9Jrr9E`__~Gm3(B8MOw}T8&uV@N4DgnEutcy}B zid-|UT%eHWv)UD>XW7Z`6D%iWtuSq#pI`Br>T>ZSg;2KzH`0VTreIZLSx|>p?9Am% zbY5v#b=X?D)S`&nBA6Q<+d9j+Cebb@ldBT{5{Ff%+UD~7*_?PqD_mz-8h%GTrE4f+ zO^d0Ki!8cas}#$8l)EOeZ-{Npw6d8^HY}c1tG-SQtkIKm#Kas^y_6*{Xay?{!-9f7 zn-t9JX&Z9ya*VqyGcB`Ivu54`0NE6jCRtH8xEspMdRVa$OKv268zS~{P&{XmE(`g~ z(cp$D+f9q?S#B zx+b_}RkZl>g#ckS72lu`=M2dW24lsYsV7WJnP|%$U68Zp0@{W|v(AVYglj7Mk}lg! z1ZVx6WrMuW-n4R-jhd#G(ug@*LV6>gsYk<$;%qDCX;Ct>7WSGdwy3bL5quk7K|}9s zN+laK)-p}g;Aa;_x|T7&qHxbazE4AJ@ilW4dqWysum_g1$~BE+-RxV)=UN*>rYhtgn*Hv+k(SfJOuBu+1Tw99E$ z!)K^dolUl6l}ui=`qt>t1tWEVo|xzQ<}HL}p|UASF7T~&m#sn6*0rn!n|~=xSr9N< zIYV6>pS6-!0Uu~4v~0dLiho&4Tp_9E%$g;FcQu?^PrFtk+C@Wfjj3Mr=I2w2p6~*E zdkSNo7M+bU8g|X1*1e>NHVw`tKcywMv}Be=S7eQ0UGQi}v~@3G#mkvc8 zN_VW~CCz%Jb1BU{CDY3JS8cR8F@I4Anw-R}kvyA?ExE0$F=flCo~QWh=Gd&8J?EpX z5H%|iLes`w5QLj{<*b9T>?O96#tpTj83`@9MT-PYLr7VS`euWn7KOGet&9k-ZGS9p%(Uf{xd|u1t-LSHnZrcjcvdGRZ z!Q?7St+44E(eSJ@HK#B)Owqd8xgZTKa;*!(_@Yy?6lO1o>2-~1HWgZqoqblq`w~>qg!h&s?`MTMW&Dp1Ew4 zw!-d352z}|4VkUQv^Kcr4U>GCBU@%LXC2NZkE;cYRUvmt%AOAx*W<>Ge5?^?H?6Wo zH+Nae*@z_O-U zUU%qbVX^X2TVZ(Qg$aO4#L%X61ER*PS3fpQlzaY1@4E7aQxW#9zvqQ}|cb>{x zXBd|Bnia8hI=-ULuV)R*qRhITu^bjH25cJwTV1Hz(EI994RBJI#I*TbV8u*oDpf53 zVLZ@EDeDwP!)ISe+m-_X_$e))ZZ(%*2kmQuNzGjX{KCiglN5-IARn`j)bx)ud|K?_c$$=kttZQEpxoZ1Cb6YEnH#Ud|Gy zJ*`M~g&kWUGwQbVkh>Y>E|6nOM){nZ*l^~T;9rO0t+apE!&~JzHj?ROF<~JRSSJzY z2%?n;xj{6nNJ1ON%nCWN?55Atd3AE6mB=(5@r9US*2}MRl8f2=swmRP`j)ihc~5rM zFRn*e>!QHCR=i}4Z)ijtOheu5XowFYcg#r%OA^zPE!SjQSK`U0%&{8Kt#S#=M&62! zJWr!6g%eBsL2O#&8XG?OoG!BNQuR9KquMz$ zd6~mo%-dEp(UzCnauOFj%2lhW4po4Tu&Q*f5`1eM|2o~g$dxXNeM<^Mk=o>lHWK!h zKfWSmwn>(i#znn%U0_>KvYHzEtcA5Ew#^&Ts}a$LGdgQ=&RTfuPJcb6STa~zCesRu zy{HTtx;$TcF`(@w5sDTGDP!qO+6%Sxz+1D%}UHQ zub0gQJxfmJf|&v634RO z7p!s;8+z}MY)vX(jp|lHsaBM=;YrtF!l7j9N)1eLYrw3c~#abtDnvCQ; zF|la0EZIB_wq{PFUZI%QNb04qXU>?Lw;Gm|p}LUOlUrp3R^_6mKD#7Ox9FldIja?P z!^AXe7p~hwb$#xqbxxu|;LWi^8v^}?BG9C=TWVIr&uHo4h=;Vn_H8gUv!=jY)Uhhk zEOL2EYGEVpTo+54;ov$=)k*>!fxgPr&DjGjIb+rnY_d(w3~f%9TV|S9lj1qpwonX< zw0Mh5TSy9<4%LQ0J{J--+`74Vdex|ElA^PA!$u-B7YQ`2v^Ak(g-e<>xz|XVX4XG1 zCN5E(OT_d_NYS7wR{ZIWxN5^HZe&9dX!_s(V6 z^EN_*X`1tKVURTFxp}2vJ)~I*@Eg*^5+^Ym$v0V!m8@vM*z`ihl2}x8H>mlgK(HxL z%rTS;W=@MoTo03$S%D>2Y{jNnpv0RD)f&@NcX(Tl{HixHr_9U+BMX$=k|Mlnwl=k3 zm{ZP01M3vQyoLu`m<6$*?on+>%p3gRJk`ENl&_fWb7s?QTDl-sZn#~ow0Tv}nG5h2 z)v=A3enm@$LBHX%ETn_88F!s*T6dc^XqINwxhPFEmD2T$cq!&wjIr0lj3z}emr~88 z5*ra=J)mu6Osif~)9tHM{VQxiGsK^_k{1ZdUf;Swy~y|0rS6SrdS2pg$sO=`%!a&; zRBnUAZt}RRK|;&#S~Pmr*-*yvn(lCe;hr@oW`n#rhPok$taz=Hi4BRmX(BcfsXCiF z8yBt9bQ>~rJ!6>@_-2ja4SQlX8=n`d%ECGB1!rKxIukE>2PoZ;;OXQ{kSh@H9A~zx zx*Z*|eu$Ec24-9nY{;9%2GidS8KyGbv35#H+AeKl^f*fPac4DHwY1TOgM-p;YmHw@ zx5vun4&BKbb(BAxeXnMn6i#x7T=aW?c#|n1cyC?i5dMwZcI6@=#&h{Ywo=zJoR6o^g)Z#!0>8 z7dzQQynf1z}HM#hN4rUo2MMZooi?Ooy_L#Jgl_Cq;WA&xP}CFOWWc!oD@AES;)`qX3U z3b|ce5w?js!evP(Yn)ycjs~yW(J7h?^rb$3Z<;+?ws8=;9?}j@) zp&Z~11d8DvMNg!|QBHMbhM1GWPD{UeNHY{^H+TEH{ZCG_A><{8YpyAX%Yn8~yLQ+( zZmi_0-V*z*a=a`X=8QA@L({67d`LbiFUYI(Dy`EuO&AiFL!HzC&Dn0k1ap`^WF1sN z+LzW%o^f~DyL1DtGGWTouKD1@aq<+WD>24=sW(}ZK;A7q9hwObWQT%NiB9$ezs=B> z?qF3twRFijNGN6p2uTMFW8A{XyaM>)yaM{zB>IwGHCv_!hH96+&6hcT(Ip*t+ zRgIM=wlU!jsz*c9{9aN~ zT;-2Sd(@-MjzB5a>n~V~`buCz)1GO||58!+*h`v$Oo3P-7r7(q9^aI9jMs1NvkYe1 z1Koj|=%aD%r6ayU%UG(K>J5KCDx3(E1ViFJ_xF9yqO09HE`5I}T%`>7i{atS2&L0L zBW)uV*gd2&uS?U*8`2f21=gf&ia!->b4~__8NBf}6fzbB_`}s4B8FfuE7@w34db`b4{b*pC+vyoFwYl2))ADKFXnHbQ zNOonXNRx&_w3=w6PPoRCQ~cgY!O;~T%|no#S2)MtfJ@sgEOJK4{m~)8faA_FN55iN zILa>Z`c31h`(}7!j&9e0et`2%cfRK64GzjiAZQ41gJ9V*5Fd2DZJ5*J}aK2kQLl|HV5NfG@PmgFUJ1rZ7U~`{$*gd5m$&AIPz0;QNXgj}C(C%(G z4_T&pMO|4inQP~EF$O3-s;a+@F-k33yLo-`PW7m8OwrB!c#vK-PT6{d9o&MW;Gb07 zRQ3#etKr`4P`)Eol$U}9#yD(hf9Tg05`CP0OJC@xVHrHLv@bi|qqfHeeO#8Kd_F zrvd})3awqx7J!gysEt&nRoHET9!sBZ#8dryA~vSM{~zn0%5uMw>K_*alQ>tdj2M zLD@`rf&}T+=x}7)xDiHww%y!k9?n)GquOew&)-WJ%S>q@r)?hzOv$?7S14rNf5S*ZPNWUprdo)wZer47B!C1OmI0EtF61~mXS=Zdu@Gy^_i6g%Bbo{LA2n&Gb1X0uD}~#6?W$W#ii+X<3b(?Zi4R+*Io+)Hca@~m z(m`R3*%j}K{?+a)87tl)>1eo<+s^JW^#{AyGsMYEKYP$WLa#ZC{zAH>9?-&djkgo~ zBg45d#fZMwTMLvRMBba|H%uCalMucp4Y~&HQ|1|ZRn*0pFpL>%{xWgU)t#zwKkW7o zTE`qCtVvsu)W$EVd&OnVkgkW^rYnTuIgyX&JEA>?wtR1DG}R{nvP^n{G9(xgjYy{* zMN(h5GhJehP}^OjyixX$uSB2V6dB{J(P&?|pE2cY6HoDmiCvP;)C{|yK5iYPb?YWI z6RL4XRb4Vq27w`wDrgIy{zx~WV4P8x;(&LFO?w_KL@L4_W-&bA=?D~AUE*HPkbPL* z;cXLjg$u-fO@Y#{8IeqL`$KJluIxoU+&*!?xyLvep7hN4XGo)}i9}nn&ru+cN!!@N z97u($C)@>bjZ&bD`z!7)dk1w~*x|0|CnA52s7F{G)FFF;KH_gKjpkAM0c z3MSWWg9S-Zp|(4p8F7?Uh3Ie$kPhZSdO=cQR4ik>ny(NaF|~z@;;Ce>ell31j=6hu zGrS(fcU`V2;*6-*eOtWDsM@FE1GamMfi~40MMK$A zRZYZZ)CKCQ;x@F(oU>{?)kK4Kj4P1`3!Q>22okQ|Lw zS-p%=X^~RO0#k)MNuN+mvili5`F728tczLG!$8)SI5l4_QuB9a2bpE#q+T z=*SkxW%pm*xf-!eHIXg|O1WOg@m^lR*5#i`^m=+FGr4|SS76+5Q7QfPl&F@18rn6= zt=R{%lZH|4l&C{B!7tf{(zQ&HR^+y&$1DIPU=%Ht;u6@))~cQx0~S zVEHK;+sp&WBB4U7uPgq}>9UtvtqfFL1jj}8YahGM!~H)I@%_mWG7ilW0h$nE4! zYoIiV_S)JcRe3j|-BJ{EJGz)-Ei=xl<;IzliOJMZY(`rP zb-BA`{lXsKV4@rybCvaF$mb8GdMx8oKvskXwC%zkZnvP_&_OACr*k#NAh*v5!~@qQ z6TV*QNdABQyiW&MGx=g>3O?ewA?L7j%6R{bYb4R`=;l;8CrgA0QiV{Fbn~WxLU3l1 zQqv6wI>J4HUPa9~%&cb0PT(Za+UYZyDM_2G92zILlWP1S?SOLJTh{k7cXxO?nPa3% zYLruyOz~@381v?dK-JZy?yz*K`|>sBKyt=7sp*qF2|H+ zDpt023#JS;c}YDY-3=3rdq7;Xj%KRV0%yyZrr$B5Dut$aMSdYwrH+S&O+fUp43l1; zVNNh7M5F9NY=GKf=n+kECsUKUDMOod$~gwuoIzTR-(#*wCdm_;GI2!v(@+G!Rpv44 zOteJk=eB8j(q&0^c!<`auMj&O1L7XzAirSfR*jNtp&Fwv^E&#d)Y5hFmMyU0`$vqh zEck2IUVqh6k@b5j;ZaAK0z)X(4vdu!(=T0_F?%gOXzR_F2?g?mw3407S50V<&GUe&ak9$UVBb1VPEL~Fd(Q1k!c{sS8>`siwdYK~*n9XHltVwpaae(;tXl5!n z8T-Cn-I*A0jWCC4Bm5yTJb=;uY@fI*RS^}aMb|jFMC!{7rzcY7$Vj#mz-p6`s^A0= ze&VBeRoE_ES&RY4P3q2iKx$;-7VqmZ@WICp*R$ z+q0rm0gVs0l=%y6Jt`{uFvH(IK( z^(nuKeN>+KQ{|M?ial#K@{YB~9lO@W7XPWz)iraUg0;5eZL25pMYdI4OP8j0d6rnP z=g5XNLR;cbekVAQ&GSFM5sr|Ne-s=9-+fPpA@`{;&UK@|H*pPR6XCWv6Ksr|_{RR} z@7e~5UmF*Dm3eZUf@E6UuQap0OsCfMfR!i&&L-r|^cnAH|Eq2l^gW`jtfzt?nm;P_ zlXKH=PdI=0+1fEI{R7`B^%0tD+v%~V=UmE`@p1Ys-Gu0E%xYEN$_>}=`?uPhnP3^} zlzZx)vhQ1@`jL_4GutXX1;)Nk{HgqsTR>Xd)-LRy_Lc3)_)>i4b`^hcTv>+KszqiR zm{ptAmE+aFQg2MnU^5Jgv|J~!P>gLI>tFu4ZF#L7Mmn-?ZL4giexoVr8Nm!F-||O^ zCGwJJ>Yq~2xu&h3?-_p9OT5@Oo=J71J7wM6_Ap zu#ayF0}8Wi3if%z^n8P3KJc8!VOJB)|EYH zo3fVdIWy)#{>q+&*XYdNl)q#bB`67{KRtbauQFy$RC8A^JktMmJ3du(HEU#*d5!$$ zK=4l|>Vd5n{r5jaud=sz&)beoa}au)d!ZMw&b+#|`B8AId@H>=7vhOxP+baE!naH# z_T>8)-wb10GyWF_#fJW^@}3^Be-ny7fkvz;d$E277w)mTuV0s^k%q2S{|CP~m;S4* z%s2*BBJGP{pwWz!)D$$7^IXT;)Gjj1%8PRqSVDaK;#m9ofre@=97JKE_}0?7<0(F~ zuiXC_)|I)L*rK2Orl6^Nb3WMy`nT{WcNuG%Cc+`Ni)eQwN3stHq7)~kbtQ^z1wIj z_O2VqUekYNV|#J0S<~P~HfDEYANhCNR{9Z{q6^NHzev6YTe@LpZR#XHZLsH?W{xp; z7$0U^v5{z-Z)O{d5C(L`4`?zug+%Xj|^19+MDJ%JBe?zAS~5PgNtG>-sL_!{`(%7 zp`I%1!l&*t+homZP2m&gMLse^-l=>IJS!H`Kk!in*_~q;X-a3TX=YaFYPZS_GEJ;S zYwfS~kWTXN|1n5L3^Va>uAE)dKbi&B>}_da=$9JC5xbe1xW}wsZ4v08ZC=mNi9V;= zs#Rzk>pEKs$R5Ju#46vafay#z5p*33AN*+KMY9onrr$Duu_x=hMz+_~|Iv#MbNxs= z-U@!oo^(*EN<+%(=_~unhPp54WhSz>;Jfpw{9YKAKOFDgXV*gCceXhzg+(OQDMGSeKvcy}}6Xsy5PYY2X$f4BTXFE`IE zkwJY@eA9o#pKEi+TcD}!ONV)=L8V`_P?@U!=|vr~v(R&~RhVl3Rxb>-9`{E5;-B(g zO7q0OJ(Mj<6D3qMObyN7-lD6}IP<~kTb{Ii+gP{GO~p&chq3P;CAapOVJz>+7MiA} zCxk7#HqSjryY>b8CqPeGe9O-`FRE_7od)fvrem0he{QO47{|fB?thtv2l$`wA5-y-(@Ahf&iP&g1fq$0?h!Lyau@b zhuyA+c4FTnXa;~1&0Vz?5c_B3*HA)svm{(MttIq`6+_ToaZd_z*O(Oy``jG#z6+r< zSac1mgSbigF#3YmaJ+J@I)yi}f&D`o*WJ>x*u4(&WM2wo6hLZ5QSr^KOx^iB`=eMz zekhgohk$RmdwAx1$`!apMY?-h#ec-m(ydSk|KKJx*IeFo&6=P=S)X_mm6k^}Cx5Wx z^#|8cyI0ZLwIl|f08H%q7;si)79(|EGwJv(VFF>c4!H(Y$sc^7`Vr)S_dhJY)-9Tw zQtP~yK><)1j)K^0iCF4p^b9)}Ptmc-~fe$@myBlQ4Js=D{(rR#Q zD1)~^2=G^r2F984Zzi!CZh7%u1wnxccncoP+w~d#xl!PnlI9-ji1AiPLr=XWcXPt{ zQ%SmV6PL;`H(Ky;Cph;kh|}3BATX7po06@+8g$}YId8bRa)qm%1yf*ntusFalH$$GmmOU~=rRWcP?f#q!@z&V9ru`- zb&r8=C81FM;93I@dChm@$5hZHD}V>6y}1pXdj<%&iVyG$QRW&1fe45j9#tVgw*1Hs zWr=@NOUsXf33*7xky{C4-R3Oa9Lj{=x<@QJ7$@T8+R zrMYk`B&s*G=(zI@x}OB!4{OSBO~82B0N?ni1KXW3L)&?YK{*0@No9dRtD~VbLl2bxv%!9 z@mfX7_jtwnO^-ZNZ;Lnum)q1TM~f0q|^71X0>;-It=s6BiBMC^AMS;_Y@2Z zFQo0aT*md_1~^xT1|c2!yUU+>sGPwEN5Q*D09)j!;jKm=A_;uf=iP*0-Cz}ANJS6@ zMSuBT=CQzOp1dYuVzk+6x`{N(dkwhQYFTux`olLvG5(OIg!gP-adS&$w^CC6LxSpV z`dHu=3I$;7hw#o7RBrxE`0CBUMHr2@xIB6bIHNx#V(Crn1ndKN*+98=Gg-=ykP|?S z8uXRB)OVXu1&m_(A;<_|x<>S#7V^RC;5-C<4}URxi=gd`uaT$D}{Lkl8FDVN`XrR943i4H0_J)z>$KD-6#LS<=Ggk z3q~IUFznFXjAs20k<1MkK;fncc>uM|J>niuNgqV=ITSD=Re$lNLnp9*zb^=o+Ex}3{IH;Ah{z2 zU;};HYn>On`SqYW$!714Qt5|KWxUDVwW~f=xO$7$ACheDCXtyyd&0VvVGwk@m zWMX*c>cS79Lwt)Bv;<#xEonX1B&h0hn&LH~;XV98<&7s+-D^Oh1(HS>oQ(Y7SF%^X zB6_Q2M0Y{GevN4C@RHf@{DkW6Ly;S+4h*0Ngb;}FYPXti$pUPKCF$KAGG8+)7}HP+ zz@TRF)vE*1zbXJzAdnjyhLW2jva3#RxF_t{yVGdB`T>CAm7R~ci*pM(Q@1>ie^cm{ zw*=R6_u6z1FFbq1&AN#LmOIJ`{Qna}V3vd317$6s?Cg8V z1r()S1x&IN2(tVm07Q+y9N5Do0C^bXFK{^}pBWnYwO3!Z@UUfM_j;yqjYTq76Od3* z0o)FY=9xa;0f(--)b{kb^0T<3an9B|t8;#xt)*a7X zl;VK1sCt&f9$|I$=+8dfNb$k-#Xye`d{jV~!VAZMz)*qM#&%aLB>-ixA3>M%S~Qn$ zl>#V!a)4^(wYOZseCO0eH?hcZ=h~_;xg2KK$0c`@s(gzGz#k%)TrGwGuuo}12c&;e zapyW=_IW;V<2l*z82JyERP|6Ml|UaUt~w6?E{(`;sTAjCD6?l$UjFQ{-Ix;mTU+aB(`^5K!CZfdI(23c?)VJ~d0 zdXS|k;zT3MFDiSLG}p3bm~Z2 z3?Yd1>QadxER*kU(Ptq{uo525T?mYs)1tU~{I*BL4g)39)t~UGt6R;!2D&WBZQr6@ZkE#Qv z!dK(qID&aEWeyk%M0XFWdsuAcyPFM%M?M0apc)uI{sVOd?*gvm7UPF+EJyeUTZP<8 zpkYyP)3;d2`4CkF*NESEEn8sngvEO^3NsI9M(}C7YLkkqQ7ylfCFWZgz-G2X$Halb zvj7+v|b`@&BYHq*ijFhhk=t&`7i}7H)Vu(cho^KrcwTZar7u-4BQn!E(@|#_a%gXs z@z^j?7|Z8R{1{P+lhBYJ&XC3YS0qQUjfb%OAhljnS%&Q z;A(eO(^h}W zVk&vUQ{1I=gs+P8<3rE4JVsf3XCzJ4ZKqQ47)c#7Ndk4@y4p@Bfo|f1fk*K99X4f< zQy$}=K2-cnAVM382Z4(4uzLt2;4=9#9$2QL==L8rz< zaS|<^O;tuJICIPgmH4Y@Jh&Bvr?SiF=t`O~V5#t%?oNF)BUi3W5Yhc_N8d~C6MVt>^n&{3e z-RaCU@KbirSJ8h|@Q->Sm6PtPQ7T%XlrpTUq6+~g%sNJjgt(07BuvJ-E6a|792ufD%GL$K{QH;F0hXSba$#SdQJm%l=o3&()ys~FjbLc1V$pN%m|Hk0Mz zX6W^e672eytGZVkPTBmobJSyq9LEkl>S4HWKsr3$;ZOL>s861 zbze1*s0WYa4hV`T3J0%hALZ@xyggq)X8;GZ9IGj;hF%njFDI9Ax(-4>v_fAwKW-}! z>cU==*`-n^B(#?ydp;l2*GawUQm-8fQL=>Y3kEu*q8)BRp8k~!ueg)e_602ymEzVE zC4qazC!;QklfWDz%z;vA`5}vv8Os6s@D(A7RV9*wgS~n#7r*NHZ*%C7(;e)*lY_&E zp#fY!&>UmJOFc+R%V*TE(@OR>&o0bo*lgMuq$>PFJlKd)rYfbf9;NYfv~pwz2`PdV zgx|^y2M60IxZu>vD4&|1|lnITkeEEV}IlK{dcRVS^U&bZ<21lH=5_V1)QT3zZo zPO0D_<0RrivREQ@$*~3Yx??>`7CAF7ruO5-D1*d~BGV-+CB@Q%ri3}YyP6;ORH=aJ zXi6P~Y}iM>1%`77r7RFX#4k=elkm~y&>;VSe}}$w}{#6 z`%r*Xc}cklufhJ0Tr4g+Q@JFf9k{|2Aw*AB?nRKGJPAgxxd_YWs`O|Skv_>WQom%R zST;#p^T(p=s77{t^q{w0vOHRz|EgP0zVZb~TV4orC(d!p$s-`j;d5U34cDF5oRX0P zAKA%^bW)Ccf)@jFQQH`fm~sLyrY2(oI>n)4Qp_vbrxC_nyu_-cIDO8^&UO)=uAe3H zBbo#}l2mbb-Xv42GwK?aWF1rioUQ*Bg_tz^7}YL}$C8{7vnXI15DlgjKp1!MiHLv> zM-zkt2#PEYr?`%PE~A%}=#;a38{Kq5vWH;A=h)jgOrEnVdkJU#RM22~+o`G}qOv{8 zqvCbKoW|s(qt-$v%OBQVL;{H&&P8Z8ELU_d3PVV_*N0C05LHC+5Fob!TwWqk18p&4 zY{#yyz67mgxeV(H%a%?ogqG{5AQu;UcYHgEm}!>y$YFhRDx_R>(vl59&H2M=mxmU1d>f*_q{{1t}cyk}By@ z!(*~KLI8Mo`UoySe}Qs)DW{i8MNYL0rpnZ##m`GZKG=FiSIZX zy{CkR_W0>+jWERzT!8V7jLt!dVx&F)EwOyn@|R#>s$djChU2`rd4N*y?3SHMjB(^= z7JnC&<9XID5jZPMUwIv#&7T$CBWnA$MKE`f6sr$r)-Ibq+j-x52NOu`q$(mC-{~!6 z5`hy5FDvkP46iB=f%qkn`zdHlb4XVHh?E&LPT5>ve(_$PJH+w?ND}x_!Yw%X*=LK7 zlFI2C2g`}}t|ah~dujTRH147yS*)NU?EplVr*tar`<&b^N_08fQ*iO0zw5IpM)gHX+Z!SV6te8iPM?{x2vh(uK z!s?|GKZQz!zKR)pHa%N;5mT`y^IiNz0`or1iCLZJMfvk2=XY;=FibM_u7) z4ntn@=_TXZy}@-Cby6Kj&>f+SL1pKm9U*88BEQ=W1NGb zh_iJEtNWzRe}!#(bM>UupE$acvc6A6DTnF8(jApBZ}8~Wo=r(IW&Vn3*WQa$PHFoUMHliwr@msuoVHmKPQEh-`BpTVZzM8%t5?T#FRNkuK1K)*ig2wkbV)Pga;a<1j~G(4D@btx+@au;B4j2e%Y!USauVk6VVue{_d<+?~Jq}xPS zikE;NV@J+%-ocr~EuKqFcW|LZB|_&iBrE#ui0|@>Z+#N8*bY7qQ&SPF%8?_#_-)6W z?BGjKawk*MVzjb) zw)!ZAmC!{YR>S!mCR#k3jYkg~E5M_%{Dj>beP_8154(0n4V|yy^%5h~&}H@sjxc?d zP9{m<_1%sOY^qii3#s<%!dE?WRLH;0P9|^M2O^}#ep1EvIuxt(FA_TD&@9j@ zA8C~mCZOaC2Y@UEoSk2}lcb2am|+!8AuVBc?5g3tqcpPZc)%-GXhgebCC)_&<_#a6 z>`PF4(NsTkm=5wnKEvSxO-<-Rn742)3in~p+2mkcnVmF)m-EMT>YzizTX-+YA31iEh#c9YFX;yKs%kK($(#B>gnxjL`q+?{Zj!S%$&`6=NQ%JE>L@|K~ zvJpb%IL3=)R!qwbv|4bq$4?UW#hO0I+>9-MsIhltA)RF51sCnN>~sEOq;yhpX@8B% za1TKtE(o48oV`3n^G$nig+U{D7|2u8{|coYOeH+$(zHdrgALaj-XR`B$359=q%1*l z@=QkZ%~I{F#!J!)qHao%KKte0Y!wC(D9g(DOy6?*ITR#yia`m7NJHg9cht7uDs67w#V+%aI^1 zEJ_eKWw=Mwiz(8K1vzn3SjD!tnK6rm&y)m5pf^TDV$C92zEXq|q=QX4?jH?4@qY<1C zj9p7+YiA?|;+uV0MQ4R1Y`anogP4v64&yf;{Yio=*{5Yp%1S1bgsMzNeWc4z>SEsx z{e>6B!j@AoxM#~Tv6fES>4Jk4N6xb1Sym=_@=HQ@b_JPakPc-qsR?7}aC;6ji!q+~ z%y%;)<;7uUBoOy_=rlqVi>E;Std`?C?J*@grbL+B#^}`tw-<|88Log3_|72ZgTugO zr1?scXWz@>2esqO(p`kPz0pBewP!_^$jhK`NV^q`2ccEK*#-|KK4U3CO@hco2zz=b zViOkMP7prUBv{#blDMg4fYh;HAVt*gYA7So9qs9TAIN=lYHnLKn%bV4%UhS*o#ad5(E zQ`!}zWD2UBoTFZPNvn2+;FlYM>kwo19J5*`eg$l`v`D~k<)=FGZ51E6657Khvg%7kL8uTsS3YUN`>F`j zg?A?9Nn<_B*=I=vm#K&Xs4_sX;y?RMOj>^^s%Syx;)G<}ypzc=#I?`)P9c~=w=acv z0Jw`J6g!R;lz0*xwDL9Y!15h6jvYY60`ymb_@_mFLR>ney>K~H%y59>pP9>;{EQsR zy84rkcS5wCAX!yIt!IY_Fr||!X=r=wE5IWIg=2<~<*X+co6d2_tPXcUIap2D*(Wu7 zZ}N~_@iDF^O0}afjA>IxlY$BcXnPXlv5z?s`yQg$oQ`zmzJ+Yiwcru+&K1KG4noPb+BSRwoZp7M_*Sb-_VxA%z~OlR=6{c%(RLqH{_nYdpwZJB666 zTMwZm=qQffJZADuW2BgOG9!$fPYOzur}(XQ9VHbeS3PHf$B+{fNp`}>G0eMo*_2m) z6uYoe=n!_E^2wcF`z+tdGDmJ1W9#sImiE=)&A4c_wAcFoB>ZJu5&nO{c#6kaj8;V_ zb6-v$Q;g7(Jncg*tt^l>F_x3zYR$zD`62h54u8usm?A`aV+TX@ z5HDbw+_$4}WG7C-I#W;_l%ieEN6Oh0IB>L5EC!{%)Gl9Q0wUnwcbV|s7J*6)QxRRV zQ1bGgOwI$MJgI|BLVRY4_mbQt>7bxmZItXB0bEn0q~?o={jK)x&83|uONPREFy2I5^ZEdv=Dfr@dR?UVPUXHZ5 z7+nx@*>^dOl!-Iu5UA9xw+$rMOp^Ci~Wkf>8D1i{@A`X#m?ul(W+cOup?JHSgK;ekx=q{aH-H!Ba6JYoH9tohrMJEB7kiK~SXQrmwpH%V(z3#l5I_;AKdo z=36J;6-p;x2zPo;aKYj1mnX?oO+-paN|i)ff2{gIgSRvA_c9itaxrT3DgDDe#1X^kyiX{NOg=sp`d2y|B2Xkwxa! zXJ=(!Deo0_!scaUC8&yFCO(xafh>*jX3honMod+zL?}z8;)i79!p|~%BYbo@aFn!M z$<*w)I!&p;`)9cnr5MLTm4swHhNr?iI7PuaB#Ry-0_>-6B0d`O3chbEIM+O>;~Om>5AF3PxDjz{nDGEWQhKb^#0H5+AC_J`)>8 zkLEg4w&J|>8z1?eS;P$4#>&|d@yC3G@H!P0Y#1}+{hUErULR@x^Z++{{$ zq|0$UlsbnC-*U%+Q)OJ)Q_S$qwV$k^i-(1)*`PT*Xbd>w#8i??#zRIpxZQk#in)eO znnaASf+LVfWcC`*K_1$NVe66MS<$JE?wkfv7C47(1e#znD3%j{Max8#gR=}!ZU4zI zWAaeS3?#i@z5;xp+`}1a$Q(7LgGkC~3lG7rmTzGXSG?AyB{m`u-< zD@QHV5|eNeeAS%Cu;v&{o?YyNmcOrobTNjRD`c5-lq#^YoK|}eNa~o*e3espoy+*e zN0pZb?VCJyio-dU!a1^oT|ZJE>TIkS-p5LetMruw15|lea-Pc0LoNIDlVnehm4X-e z#sUc-#5Zc;S+D&H2+xu97pS{zT*~dc#89ZQ5mwzv4eB`YE(nSl#gXej8qzcz`%u&J z=Wt57l?$RQ;- zA+c{U>&f3W=$GjR>iI@cVt0JcW3zwIPBuY#8DwEcJA_4mEQT$+xY$c|(><=!yy{sIJmwNqSoIB#zGRgt+E1C{IThK3&|@t@ z*~MRF1=XW*6lzp=k=cL zX-NNJk>r(Pz zBHK=^#;f+TTYV1r;p-KZavzl$+5zP}@bLvPq3Xa_pV;-8^Tg6n0(7N;4>h}D_AF5o zvd~eQq~ZG1VT`Bxa(OPMz$vODcZe&7TmlM=EFqcjL*9<@AUcHF zTzXfC;e63P0TtBP1r_ZJ!F^bL@zuy+{u{a=xWOZ$!=x-c6WOaIu%yme-yYQ+iiTsz z!SEG_XaQ5Hzg7B4yXX16KLAsSKi!Skf4wOTk_&e$`L5bn`^i7HK$^yhuZK2tud#V? z?gUUM-3qm1BU!in1frYw*w3}=J>N7sb*)XkNKZR9KRNrHKk-uS*oTEts?Axl+U0?M zR_w*z0p4j?H=3pCz4+vLf!X$*GT`f~XTfu#i!5D>;;VC{T4kRky>L&|jjpr3{2P1i zY(XddTr|lKeBJ1~xee-uuBTTWmpkE4+q3sW{c8Wz{LNo_uD$yH(yMrydae&S&-$ii z9{!Yfoj{Tp7NUV-uAg&XMI%rPv{-+4C?90OJ|Y<@X4z$a;A|@Y@ESmu1q|bEct0Gk z#ql`RA4Naio$!0^w>xk{wm}%}U7Mk^ zAh5nbb#{w$#ZvTCc}Y&KLsOqS%6`~d27soee`nx%2V2Rf`%StCZlxP>ANcQ1^~JEZ z_hlatpA3wv#p*MaI^d^aY(t0q5m*wae5f-;vLi0RGsX{BMP>Z|a?8|J>H{T9^x-MLp#x zxCw&AN!}?nJd6C+H1m(R+c*HqfY&Y)OY=C4|C*#7L&_?BNIt=!h!PIP=@v}YTLmWhqKnd!Ti z-buA%9c0G#S)uQ1BJZKOcq{BFm)S*PW$a|zqK#wCABAS+Woepf#+vTm`H+6l~Te%2xgF zOhq61Mq-%&z)m<$yhgX`ZoOd|Df`jizZSgJ*5WqrcY(Nz3+3pb*6erSNlXMZ=3ozv_uETiDe;x=N_LX-Cq`Zt@Zv9qC_ zr2pcpV#(U5UToXYAU^Ux8OQZT_%rpa>hhb(_t22rLf7?)cC24yd-`SM&C)0>{c~4C z`KQLYrf((dDOUVVXpwKaKiDnPm_PQdgYQUF(JPMaogA=3yjMdr_M%5G*ZVf`t++y$N)c95R*7e0N1^vc;|uVst+OgM=z#qZvAWv1BhHt5LRO}*$k)roMy=?I#F zv2`o%a0be5WF#K(KPpRk+tC%iamT)995Cq09~rn`qD@c-^i^BYo4V&;dp>!CY{U7g zY$rccUDh(z)~-xHd#g6(W7SsJO8n;k_$2EHM)`LBS^f)~>Yp2<3)`Dxp#Afo{=T@Y z`KOEYJMY;&3^lB4f4BG>b3se|9)GqxCzs)w>6d!ZSKR`6_W#Zd`8(~zG&S`tIn8{( zxOR7ucC>{~^3SCuC>b`vp19*`q(Spg?*yLEk>M=@wwWZXe*aVTQ}Pd=)$jR^`NjBR z`fsvv zHq}|_xz-Q^{b>1>HTVDikiW;_Fblo9N7i+|>-m?K_7C3gwj?i3@TO!2rgmzU-^SN~k1@cfJ7-~24YgA9Dy-bD*fOF1z1EQ@-xHq`$9Wwx*W z(5zs)Ye!!sUF|CHQF>B6Yg>kiedWtd*N z2E3+j0Qmn5ottK^xoXC5T1NIix-xW1&FYffR6Q4_f;Yt?^;Un6d`Nmeko(An%4bar zSu6VLm3CVgRX>#tZkq)@Ja+ImzKYhFSLutrg)WQT;8Pql9+GGC+A_61A#45z_XF)o zCj15WGxzq>uPMI8K6HcVtLr&C^1t)`c3=EzgMS3w-ICu=0wOB=h_t-zVBhrY>sV*e zQTop>R6tmZz*-PnTA!0E>8op&T-Up`Myip7_ZMv^KlE^onrG7ocmmq;H{DydRqJPa zv7YRu`WpYxK2?E=H?{>`=YRQP9@yY1xZg}*>k6#ugTjZbRbC3lv3cd$`x2hy=e8!= zmo9ys0ATUTiFqj8M!@)19v8Z?c6{O+dHP`YdC!j%4d*o7R4w&=>q<0^PLWUbDzfr6 zI89qC()Uk|ZT5?D6zjrjn+idQ;eFBlzBlfZ{53UHZ(G?6HiM!MaT7~k{(3X|X`k4KfF*Z@AG*1uA)f+S+V!ohV61bG z3mtc_@&WRUiFzZRioi<}{s_DjhOSZdRozGNn~rsglcOGmk_s1x~9FY%>hURkR_!{z-* zwi1)@Ru5(|{i6J#?#joKxuakE-)6y{WK?gPXPMXfOw;DRWdG4;WTtKvhtc=i@2niZ z-BfSbzX&TtzVr<&%TT}AkM}q|$y&G;P7||8PqE=Igs+AMIt&ccE$7!3RR^NBq?!Cn152OXkoB^Es3&bzdeRB6 zRcpr=rIx!98I_mWrfK7X4az+f&FrtvwdFtUS^tYU_et}iU2*^Hi}MMY)!WLRXzO{F zzGjCB0H*!(*h&CIbmh6;6m`*G9Yuz$Rp5y;khg84cso1b4q>XzmdKdqpNo8XNH9&Tz zSw&Z>7b7T7)BR9K_p>Q?X&cr1-jV5#|J7#s!#*%C-T!XSHS+wascfxy5BAFM!M5*N z@P@uX$gp7l@nyIx_}~nRe{;(38G-LEeJEDRksb^Hp$6+!HZp%?+M*A^L^(FijD2*F zpBH=lZJ^KQ@nou-Fp~>67>)+GPRAZtpvK zx>>d(9wkTMihDCnLYwNdw8>p6-mGxCnOf>$agZN|H_pD~O)~Z`OFiY3HK_gkB(^sH zXTWiLUi_c(NvR#}a6V##%sjfOxAVXIVtev!Q!VS*wsg-D%j{6yVK*!Q{g?kgj_x~V zwm7X1`-U~auv!*}m<=cd43QN_4#rl%3bBJ}HYgZdY!pC2VNJkJ9I?Rx){aP-ex{w7 zGwn=0)BDVs-m5gd_g?SY+uV0fdH+wIo4>Dg@7$U5`<-8Tp6_QSA4d&uT6&Wo87KN9e=Z&6Polq^mGsDL#>bCiQL!{2GAl9-VZmlCW?6QTT|Fr~p_bHwd5nUAs|pNr*yz-ufSsHBL) z7!c^~kuYJo0@QCQoe1+8y~6T=pf=XXjf21Cp;JyT8+78(R25;BzzPYBd@AR&F=-N7 z!JrejkoMokGc@kdLeDTk02$9f-?ahS*P(qzs1MOO!UCRKkr7LM ztiY0FIJod?gf=T#fGHLoY{eI$?D_PEKwP4TqWHFi&?S|R6&gezaxY0Gq9!^cv4O5C zq1}ZfV>OqcT!>cN0l6;Q=!k%%GDZg#iK!@UO39!E#^{u`8wW;{5zvYxHbBows+2Xd zi4w|0xX{)ImRm(ufyQAUn+J!P2mUY#GB9c&K4jodWd+F(p4|<-+ddlMI2jIfVymrM z8il51PE`De$yp)v7smnN^2(qu6v#I{E@I0%4x5xqC>QvFRW5W#dcey=Ku2>ps4Vo? zd$~Ae&gK~#nPd1uJ3~bH3>t!dCUyBkfq=%(ZoIx3!SEki@j5TrOOq9?wMvsY# z=j<%E|*~J*0<5>=5gN&OonU1aSnQ zEJBxGDh)vZG8v^+_$dnjhYC!|Rsc)mq@a^nkYgeq4IQkQS+70#q44KvUD(T_X(MG2 ziY}=lEyy@%TNb+eiwNTog(*YP!4NJJQ*~3<0iM4%3*^MCA){8i`0~ zt$+`}G5&!%991d=em2(@~Bb2Dp)OM*%)BR#+n81k5?f`Yh0op2ujMbWs*N zPFKqk=*QHuVxEFYi*syFEN2O=ic#qE@bv{hP*V3H%S9NTx=NR2Iwr!Yd`P%_+5p{j z#bA{-$ue|UXkY|9E}OJhsBo^xaAHv!spJ`>Kg)&$03(|OtUjcneNsr4FqLV(qR5)c zN~rp#j1_Rpr9kEt5NMO<(WQLwLts_18BU2&Rcs8wl~SQ>ktv}9$`EqU+(MS+f9Zij z7z6|QOIl!jM(Dnam}!3uxN42`1NGu8meDZsV4B4UI~7NBw7`{QkYtcfIjl@9$7c`+ z5Y~i`!O8^fi#dV8;{)m&0CreS6%%nrs>{+c3aos{8L%;?l!r>@srDXE>y6SHa8iYN zAo|n>D$cW0)oOs%$<1`tAY>3hk@doN=1KuU?6os_+{)r1J&k1`guxC`8o4ZGhIjzd zVP&%h323-ZMi`B~+Vr%hP^3cODvOdf#>xSmEtaLNp)j4a>0#b3&=_I-GR7G)Bw=KE z!G~0?ggJxDhG4ENdh9SjMJ#FOetCkZbP|3-tUzaT4#p@DQGn^P#!!-Vz*s^20{F8W z73C|zNpnDxeT?O1?2i_lR%GG~Y8TTAEJGEtfM4eLv|8=@K+5o)%K)J>ra#0yi0DYg z*^CIKbcQ19S4A1CD8`bq7|jW&8IP7=)Gq72fXhapOIlscGxjn7l9@1L)%%`9Sq4#I zY#{Do2n`sSd>sC>E(VdpFQ1XJGO?4+XZ@5Rm}NB{oQe3fA7~K(QHvNpbcW3(Dj%pB zp$je>6Xqx=U7lbnT7anw@FKwtA7WAiEkLwf9A$ip5JN(Aizx>W4dh}V@XsVwN^t9w ztf*K8`OX9JB?v9^h(AQ`JR{0Q8KuVmA=RT)iXfRF$f<6>8zgBFO(x9{eFAfGI7i1L zCdQWW!bE9ev}EvuBM8%JtA<9Pxz|Oi7>Pyv!8ehRqEe)=e1>vJrA!Rw|3Eeg;-7qx$QH0gZc#sf{syJ*MHPb!=L0KSe#>zFZ7O#NuOJZ!% zZJ>=|0obo8+HNG6G$Mk4i44mtxLE>~vu1!OgL;h3MiDBd)H9xphqhG&tihjRdC*YI zHAD7BPXECRfFx>g(yD}vk>s3|Q)*ztkq})GRX&gu`b=h7XFLLMF_5{?5zH5ZRq4^e z)G4L&&|F{9mKhBSVJ~W&^*KNW2BXjq-SaSq(RN$`XnXEM6!9~9SiCr+80{*^Xe&QU zGPIKvfS@>Wcb9f@m2O1Z@#0*@U^o z7;Rz7Elsf@gP(HpC5+!Dr>r4(^5RuG7I3f})JBD?1v=!kJgJj33aK)BmySlU2>kOh zHs|0_MV%T@Wq2)x$_#>}7-7bLk0t1s1ZA@!gm&6JA2OYS35cpx+$dxvdI4((YE}_< zFfr(x770DHkF+otw1gK!lD_Rd#D5mv9u%&xf8{F!YnQ)w4&62HSK4G3a*_wOt?_4% zzIJ)&JS9i^R&^)n=az+41#a1ZG5GDa=hxP@NonZ?+(EIjw6K~M_HDeK_^YGR&rT|T zzBBL1y|?CEWa`FMye&GHr%nibDs1$-^32zFY{Q-68HGJq&+z|f?GN{und{ctcn!r}IF7&h;WZ>@*D}T$?F4on*vWoon zpRG;3^e$0Poh3_k-MTfj-H_&8`QCxDCxg&P?>4wCwbe&?OaCv=>aAvuwv0>9t_Z17 zqGsinPO+J7=~_7s(HRM;uHKrjE*?il%8kG7ojM>rQ{Tq_HU#5a8i~GTYnl{Kj#=gq z{98MI%{kJ1XAth9O%x&qJ;zin0|zm#RqBLqkw4wWN0F_)9^AX?_`yEH>lkPj<%MV$ znCBYyn(4^>(S@|1==fHWwRRpGY1c`J1A~M7y0l~I2xr+}-8;7Wx%XUv)Z)~-)Kv7V zljy#3EDm``Yuhu|Ow4`qhicB9Z>Q=a%j~KQDSSe>3*#;P3pYeg%G%bo4Gz5{OU2sO zNVL$Cbcui(pxkrdqer{xHrf-Be*s`2m zYXhWWN;{A?w}T6pfff?YxhH#_*c%(}x_BYnhFiR$;g7!E!VZa{ZfXYA!M7F7eVxcW z-V6P!IldM{qNBZq4OKPs!M+h6+>PX^xaZYvN7pQL64fmm#X|a*EpIKcRLr?+LqB$o z47h{TR@^i8inaJs`G-sSK-F`NgmYEh(T>-7>mVFwn$MD6@DOifr|KZ|kCwrmxtZ*x z$NIDN;5~RIv3_=%n^pVCnzG?sMbEN@WfL6wRzb*f#*VU{>BQM`d!j{o9`4wVo=x;* zUFUiQ2n~;pcz?eg9Vh><9CQ6oHnlDN zN!lrON<;b9J`+Opec_xMXO|AZAQJV+K-N>PGZ2D@_RVW@0a(gW{Kd*RBY(NIPvIXO z+rQB-kJQu5AUPL9Vr*qrm`R4{*wyp=>V}+hXSyHlhJn7wbU<-f*nfGisFzwg$mtYq zk#)LPTp)01Zx9#~1J@q?9~zum89)TfR#}JZtYNj~?P7IjyKunw)s3N1?zp#b_&83lCC%yx zpL+(J6Q@`1B`2m8cO9KM`{6$Mf55?gaRKeh2$2C<9xioMF%{0A$1#~(RrMK}(wZrruh7^`!3!oFfBA6ejw4%Yl_&XCt~ z{Ii*?Z~Fe3dJ}BM>evB2nRdx_7$S(XOA|;1x{-2eTe+r+i2y1&1Ow-nr@9rNaoXri zT#Nj}zIkivW_Fo<;J|NHXTB{m_Ow(@Z#}XpbrLJh5rI5T>y$fiEsSl;2x-K&f|d*x z32?D}NSz~Vv{|0E#$pxCy`ZZ<*h0HvsDOKC1MzFg+?UQu4uk7RV^bf+8#*u_jIwYW38wM`@xoHqW=DGZ;JKsSl-rmL>+vsJ#db2;=M^8 zOe1f-Sc8b>3B*PN4V2v4dcvAy1XE2ty+8mI;H-o&m;)n0BR+NYji>6wb1qG6M{h&b z!G@k~Z(G*1w+yxL$UZH=^_%Pz>UkjI8$BEAnr-CAVYmURvaarubIm3^L8ee%w5x8B zztQzT60sRB*}RMF+1*E%=D*k}j{1=WFr-vXb0)|Bxn-traGH8}N{ORznu7!2(73oD zcis`KTbjhze+>SQ&Xw!0H{1t9vwU#XDawlbnIPm6J&$Yw4=_S8s>Zv=TMSP$g=_Z<)bslbe z`m(8b6=;1iN=ysc9`6`8+_Pw3Y1*fiNp5J^`Qi7A>?-@Kt4L&b*E7WUOVkqK#wiIoOu+|8$}`8N0r=>l-`n#CY)4h+khyo5`(yWo#yQ!oLI8 z@|vdcke$aHWUD&WL)Dt-$XobMWFxra4dwF!ER0n@U4x66wtbI6DMxWgz$BtS2FI4E zbp#iYko=uKiiYZ@S;wU3`Wy6{du&h;#7{Xx~!bxmXCN;ir?lxwywoX9qy{;cK8+(^*m z0Co~zp?%>w+~M^6HSf{U=l|0-47?fAkx$*7!cKfp)k}LpLpo9~95wFLb;cH0%Q~=t zGgfV=mXW#iECd2XSi@SSo@AJv$j~2E+aLv#Tj{>j;%>$0+ zJyzzXW)W_gU2{X5^W)QElQUDUik<2YQ!~7DA2hOXzcD~ssdcfh0%geuMn|k~Z2D^X z8a|b-dEI=UbM&_Dy%>ytTi01noNbHzzyupM6siss=j=%gNKdIPJgEELjqC&!0&wqw z9MQF9EE&X`5aZpj)eReQBiS&_a&3NHG17O{bx}LsHULkT`1i)%KW`W&@~yj{AA=+V z6+h13t|1_o879gtzveh1OZPE;A|M}IIC3}b-`r^z(w+${jPN+v%>DdSv`wCoqhpQr zkyCYC-Y8(_8~P@>T49-J^LGxoo`dM^ zzVKI<+W&1>`9B-weSQ*cNk`6gY*A_u?chicOLtpcI3}jHt>MhsIR1mNux4%AcZOAI z?im52_uDfNu=>)rVyj&QyW+FEBVUyE{#CNBJqvq+uDRnp+E2ckduM^7Psh@CFoXJ( zSQh^Km1?it=+2oRnP^+)0k(9sZA1UcyS2=mFm2~+xjw`-0}Pkml8wZ#9ia3M%F41( z8JY#TNiZmG!vha2J}f88!ZnN>-2-ti-BQc}5`l+Z2K%N!GchI?!n1oJI)>J&R;Hs` zW{-ihX|LdzfgKnj%o%)I(WE1dA2Hvf9;*Djj#T zG^y?#kPHp??O(0?xB5D=OZTxRvW2;9RN9bh9@HnAmX&^S?ENb(-5wvSm%gFuY?+o0 z!3}rGn}=ZS%WH!{H`R;`9T*2=)m}7AwiKJ(3Z}=O=_b3nzkjlRb!h8ZComH?Q;X!# zeD)12&DeK_>bkxw*s1rSNqGtj;J&6NTl4gxDX2t;F2abu4pd&rUqo z?(=iJ&R<~L_(t6G{QKM3va%A7-JKLH(@fLo&i`AD$R@us_mM-nF73l8os>tSrWOVf zGLVhQjs7<#zJq73IAv@20p?k_?tA*$L9tbAaKM*y*9MgYX&BPqj+IxAP375bdrG%Rw=m^(bbJ3t87~U!hn1b`5z~u zseJD13%-oJt#*zK)jRLP*eUgef6!O%f``~iIH|THo%Gt!3=hjac}oOUKgJDz=B}Xw z00`^JI^J@R6l((BjEmY5kPu9_`XpOFu=g>~vH1?H4WR^JDCW>urWNgUK z*`{(PX%*Y5w!F#dA)=%eQj?ZTedvV*Su3( z&G3KM^6+X0VzZ-$J(70}^$TX4X}@@4D2(g9qFQz%1SqO zG+{?M$*;v@Z$G(@o?;F8##m1@R82pWr~7J_g`%M!nAXmg<}4rT+fK+`ukQ3G%`O98 zDIB#dAeVkX4i$UOnFscoX%ZfAVZm;PBy`UR8Rk~SuXO{>z%=x=0S?5LYMb6k`?0ln zX4T=|=2K;H!L0eyRP5*u4v#S__dcA_W#p*#v5strZoeasBFzc+O6!lOzp zKlS|XJl{(lL#xVP+X$DLwy0y7=iq-IA9)+jiFTp=c1ySQ?4mvO%Ge_s(OTschHayJ zC;aIZ?0AO3b>X*0L{s~BX0j8xvO>+4b*DIiO>xv5;eEWQHp3g<%HByr9cI37nP^aG7StopnK%uk?~ErV{yy8b@l~yJ;)UN z8s6gUO~%};*l|Gj_PV>3n<$U4fS$(ZXkER~uT!T$Q?Z4p*$@n*4>uAM!>oAvCN>JFYA)axB! zkv%V+b;n}Ex`n#27VlSf`a`a-fVyiSAZN0LYwunOJL#Qu#Q!?f+*J;`X0@x_7+1xA z+vV)=ZD}cQ7iZORZi094m3$!>;`_v}{Pm8qFaM*nxL-U~=CL7pPJj)VT!A1pP404V zwhk|`WBD8e9>vfw&)@)PA?a0Gp?z=_tLNdZDQ_ui(zd+|XZYj5UIPcZmW^^5|MRXH z3b~`B+%)|!8`e{0tOOZ4R#%Tm*mxst|Ac7f&Z>b8xZcu2uppPV1*apM8~de$y=EC= z16bw$EbvLd4lV58^G{dYRit6>T3eh!a1xrpS-{=_r=OC&sG}XncG{U@=-Oyzxl`^C z8brp@ab{^-S^y=Ljg1op6q6~I;qULNj>d*=l08P&>YB7kjQHc|kJGAOCjaU;AbfVf z>jy{Dwr^-y3p&ykx#p}n9rsT*OeY}4c7_Q+f8X;vKpJryiK(uUIVNkVWBLqY<}fo8 zw^DWO)>d zo6=IZHtv+LZdLx=xwsJ59VdA|P}9wINB_^Ss$WdGX?*SI#E05xd8qDq$6h#xMJMhZ zF;49>|Dd6n+4i<(u+1IBVL`@e>$l{-G{+CF|Kx~{#Bk0VYQ#aBL! zWGCt)0y8AGG%RIDT^sL^4c~t_E)Qd8*xbX4EjAPMqpk8r)()JME5W}RBQr^p+wrV? zBgqQX!MT6MY33)^9e?9nCmQM+aVYKKtIRBTM(#}0lE}X-3UwrcP z^fmd+R>x*!yV~Nhj- z)8d}d0rI_5cboXWbs28Tsz=?-333XEbZ}SzMUfk6ExZ-3`K!vo(zQ)^1AJ&3i>KbX zu^xs>>{4GobhUuKZK!s+E6&`o?0n+ng0NeSi{*wilIdp+4kmZ@f+*g{=-%iWJc zS$}zDt&4wprQO(i{$KvGmuy5&_Mzq1q0ND4a7N*UY^)nO>)5c=C=NMbWjjZLIB0JH* z4L^*ouJ5P2ylLVm08=h}w}C^!rukrO5^d#5SPM1-YvF;nm$n?=yHs_9Q*s-s>EL}+ z_(!MMNLF_ar6cc2b@C6g3o+D2%YSjCpK6ERZGIPRbLQ$}w39sedj9`>u4qT0ia7OK zeZ^b^=%Ax*{*gwm;b{3_nH280&()1&rR?zz!47wC7^Qd7fq9Ru@QHD+f(J7;bV6eV zY;)S_Ce*Yxyu-r63j?xH&rH-4*UH_L)MRbW#5(hARdfC{ISjT(Y*$&h zmYfOnssT@!T$P&st$L}eONP+tv~mwQ`%F#HPwh;<04I>Lp|b_OLmgem+e-ZEtlA0A z3Jcy(0a^)zwj&ySxxb5?q6-iV=XoC{N7a13ib?jvgKUB@mV*utR9Ficj8%9pzqPQHvGw7 zv?}A^5-wpDR>0;g&0rs$CYNw**N`rZK%K@0niIEX+gJYEWxONWksC)}*vOA$yZoOts&(7% zPpnO%893(l<)irfy45qXQIDJl89-6SKNxEcV08W2Uu{cZdF$?}YwnG;mW01U*Wp12 z8JyYAjSc7~_{FgY?)kLy@>25MZsc1noKf@UANYQeQgDy7?uxN^U*l5NwpV+X%?u045JiV`oEq7ER4l?`l<1QkX*bg-o}^>_!rO>PsHdiRY{hMWoI5hK{G&3|EL2+7 zm1Z7;lK}o!*ih{~L$c3rq+q-6f)SINg`pNX(1QMvN#(#j2-*Q?u;tVo2Tp^8ov`Bn zS-JX_Ix>YhwrgpDgWQBg(>~YFZHaDq&hJJJk*@cT>ah;ol?`pD;!X+ukm6MaUSP3S zXvu}eK<=N-WnJkJI~n1AQn|xdrH&7pSFLmN8X0D{-koTLO<_VgXC~aGW1`*g8XQoi zm7v^fCpPE+l1+v1k`W!%&|pRW<}$ia%r!sOw$;*SQ^#;fZ}7cwWj|<6X}I?aLt`x7 zmY<>Zw_%z@zPF(}i00PsoUk!kPt?Ud-tW~bd;SzQ=nL;oy+!)@p|x!p+Q-$i_SZ(S zp>Zwy;%YSooxoa9v!6=PrCFT>yYjW5DVrE4BIq@+)QcQoSy^M zHHkDmO;20$-I;%`-thLKiDFu6=>E7~m>3$N6|b3J0{YfUeigtnxV9JQ#BcZ~A_Bs+#cUnw5M-?5Yj-Brul!AT7|;`Y8bvtguyimgU$3 zY}pZFc>*oeTuIoHIY#Ax2e2f}OhFl=mHs4Ec4=rnVYqNW=7JJs4k3OQalxhXzLlVU z5iiklhmh7m{y$!deJBD2#^P2|W=Dn2Web$fq`ZVOti&jw^l2%}3kj&INlqEiFjKJ? zScgDQ6#+OZ@Z>b&u`>L0o>l}&wi-0kVLe8Xz!De${7TY|O4K1;}po@t~Rcub< z{@@W%+lVP>S<&<%5lJb~#~3|d%|?qPop)szUs2CERTY+FK-hu`Xr#20CNnk)Gm6-Z zBtxq~MJZ_{Ow6xjln$KMYSnZ;?WW~`tt7E5Tfxnb+Sonjpd~6H?boEKbY4WMpeoqN zCxGhFGI)Z|#Bm#=Nak6>0^cV?~N&^lfDJvLU3=)}0 zM&4i(a&V2pNR3cyN4RNjoz9$uVH z=uj#vm%#HKqTRkM>neCD1Gm7Uau=;L2dRn*WphynrP8{Yj5*3w(bNaROHh#zkBueF zAKW68bQ=|P4lEbD(ZrV0SytrMuwlZ(RP#WO#YifisnUMEoFYjtTh3Kzjl|CQL6Spo z*M~$Xp@Ie(>w>ljxlP5E$PAT3?X;?_VM+=;EtPtyg2+Z^t>F(e?qMTU1ta8TXr7%< zX`(ozsX`s4SwfdAW;!G=vtmh(a!I31B@(90mNMg#DQS*S$HsAl(i8x6$@!ryI?m=% z!L6ORy0TQ!n`6xJ$TOK?jBbR+twlQKZKP{G~shkl^J8_Y&rXtS>&ibo39ny*zw3?$$R`{`@tlSNXSt+MsB~=ZTfF_fy z%EkDT8OoRQ0f<&)Rr(U6v*##>B1Dt%Fpc_@P%Y~DK)f1;1nQ3viohITJVF>PCd-Em z<-q8W(=oitx}bDl!1GX1HOi`-cBZ5+d_fD@d{joOH9*~|iZ7th8OknjbX1_)*yu!1 z%aYD79+!aOMiEvHZ7!v9mMsZz*6d8vW?PswD&@3CPTX;!x|8c-MXmsqj`7*FfX@a{ z4QuA`K9n{Njr(M5pdhF19w8k}YHkSUt5``((q0O8q}ZH3M)~|1R^-soNh2RBz7uT9 zXoP}iBO@({StpkS&?e354SrTw=1_TFn$hqil*ub((u9$Uk}!_)Y1St5zo=ZSv>2s` zaEvJ{@@&Z$rQ8x5t%-}-tUyJJlPH_PNs9DkC>vkErd>e_1KTmjtFR73gjI_GC`{NG zNl`65Wok9Z#HOngNBbt+)1hNGNnqYKE%ohv`k6al)+C6L@7E_;8PA$nn`oj zG<-KV<}HL+O*TLyPK=S*3FyJ# z->AJz*rfjurZbcpBk6#gOGkMzHWm!BP6^D40&p1}YSybwvjQE5vB)!YvRYvM880J~ z!64@908oO#3#z263NB_cNG0qfrPpO?nZ!hCfi%Yv`D-1YUKd??%&_#

vKnpXRob{D0Fat^`y%GHo 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

*rS& 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
yHDa3&MaYsJxg9`8!4D=cUY4z*AEC6a~5hQ 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 -- 2001-09-02 + +This file is UTF-8 encoded. + + +Danish (da) +--------- + + Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen + Wolther spillede på xylofon. + (= Quiz contestants were eating strawbery with cream while Wolther + the circus clown played on xylophone.) + +German (de) +----------- + + Falsches Üben von Xylophonmusik quält jeden größeren Zwerg + (= Wrongful practicing of xylophone music tortures every larger dwarf) + + Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich + (= Twelve boxing fighters hunted Eva across the dike of Sylt) + + Heizölrückstoßabdämpfung + (= fuel oil recoil absorber) + (jqvwxy missing, but all non-ASCII letters in one word) + +English (en) +------------ + + The quick brown fox jumps over the lazy dog + +Spanish (es) +------------ + + El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y + frío, añoraba a su querido cachorro. + (Contains every letter and every accent, but not every combination + of vowel + acute.) + +French (fr) +----------- + + Portez ce vieux whisky au juge blond qui fume sur son île intérieure, à + côté de l'alcôve ovoïde, où les bûches se consument dans l'âtre, ce + qui lui permet de penser à la cænogenèse de l'être dont il est question + dans la cause ambiguë entendue à Moÿ, dans un capharnaüm qui, + pense-t-il, diminue çà et là la qualité de son œuvre. + + l'île exiguë + Où l'obèse jury mûr + Fête l'haï volapük, + Âne ex aéquo au whist, + Ôtez ce vœu déçu. + + Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en + canoë au delà des îles, près du mälström où brûlent les novæ. + +Irish Gaelic (ga) +----------------- + + D'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh + +Hungarian (hu) +-------------- + + Árvíztűrő tükörfúrógép + (= flood-proof mirror-drilling machine, only all non-ASCII letters) + +Icelandic (is) +-------------- + + Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa + + Sævör grét áðan því úlpan var ónýt + (some ASCII letters missing) + +Japanese (jp) +------------- + + Hiragana: (Iroha) + + いろはにほへとちりぬるを + わかよたれそつねならむ + うゐのおくやまけふこえて + あさきゆめみしゑひもせす + + Katakana: + + イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム + ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン + +Hebrew (iw) +----------- + + ? דג סקרן שט בים מאוכזב ולפתע מצא לו חברה איך הקליטה + +Polish (pl) +----------- + + Pchnąć w tę łódź jeża lub ośm skrzyń fig + (= To push a hedgehog or eight bins of figs in this boat) + +Russian (ru) +------------ + + В чащах юга жил бы цитрус? Да, но фальшивый экземпляр! + (= Would a citrus live in the bushes of south? Yes, but only a fake one!) + +Thai (th) +--------- + + [--------------------------|------------------------] + ๏ เป็นมนุษย์สุดประเสริฐเลิศคุณค่า กว่าบรรดาฝูงสัตว์เดรัจฉาน + จงฝ่าฟันพัฒนาวิชาการ อย่าล้างผลาญฤๅเข่นฆ่าบีฑาใคร + ไม่ถือโทษโกรธแช่งซัดฮึดฮัดด่า หัดอภัยเหมือนกีฬาอัชฌาสัย + ปฏิบัติประพฤติกฎกำหนดใจ พูดจาให้จ๊ะๆ จ๋าๆ น่าฟังเอย ฯ + + [The copyright for the Thai example is owned by The Computer + Association of Thailand under the Royal Patronage of His Majesty the + King.] + +Please let me know if you find others! Special thanks to the people +from all over the world who contributed these sentences. +? *Unicode Transcriptions* Notes <#Notes> + +Glyphs | Samples + | Charts + | UTF + | Forms + | +Home . + + +Name Text Image +Arabic (Arabic) يونِكود ? +Arabic (Persian) یونی‌کُد / ?/ +Armenian Յունիկօդ +Bengali য়ূনিকোড +Bopomofo ㄊㄨㄥ˅ ㄧˋ ㄇㄚ˅ +ㄨㄢˋ ㄍㄨㄛˊ ㄇㄚ˅ +Braille +Buhid +Canadian Aboriginal ᔫᗂᑰᑦ +Cherokee ᏳᏂᎪᏛ +Cypriot +Cyrillic (Russian) Юникод ? +Deseret (English) ??????? +Devanagari (Hindi) यूनिकोड ? +Ethiopic ዩኒኮድ +Georgian უნიკოდი ? +Gothic +Greek Γιούνικοντ +Gujarati યૂનિકોડ +Gurmukhi ਯੂਨਿਕੋਡ +Han (Chinese) 统一码 ? +統一碼 ? +万国码 ? +萬國碼 ? +Hangul 유니코드 +Hanunoo +Hebrew יוניקוד +Hebrew (pointed) יוּנִיקוׁד +Hebrew (Yiddish) יוניקאָד ? +Hiragana (Japanese) ゆにこおど +Katakana (Japanese) ユニコード ? +Kannada ಯೂನಿಕೋಡ್ +Khmer យូនីគោដ +Lao +Latin Unicode Unicode +Latin (IPA <#English_Pronunciation>) ˈjunɪˌkoːd ? +Latin (Am. Dict. <#American_Dictionary>) Ūnĭcōde̽ ? +Limbu +Linear B +Malayalam യൂനികോഡ് +Mongolian +Myanmar +Ogham ᚔᚒᚅᚔᚉᚑᚇ / / +Old Italic +Oriya ୟୂନିକୋଡ +Osmanya +Runic (Anglo-Saxon) ᛡᚢᚾᛁᚳᚩᛞ +Shavian +Sinhala යණනිකෞද් +Syriac ܝܘܢܝܩܘܕ +Tagbanwa +Tagalog +Tai Le +Tamil யூனிகோட் +Telugu యూనికోడ్ +Thaana +Thai ยูนืโคด +Tibetan (Dzongkha) ཨུ་ནི་ཀོཌྲ། +Ugaritic +Yi + + + Notes: + +There are different ways to transcribe the word “Unicode”, depending on +the language and script. In some cases there is only one language that +customarily uses a given script; in others there are many languages. The +goal here is at a minimum to collect at least one transcription for each +script in a language customarily written in that script, with more +languages if possible. If the transcription is the same for multiple +languages in a script, then a single representative language is used. + +Still missing are transcriptions for the items above in RED (in at least +one language). I would appreciate any other transcriptions, or +corrections for the ones listed here. Send to mark3@macchiato.com +, using the directions below: + + * *Supplying Missing Items* + o Most Latin-script languages will follow the spelling, and + change the pronunciation. For any that would not, it would + be good to have the alternate spelling. + o For non-Latin scripts the goal is to match the English + pronunciation — /*not*/ spelling. Above is the IPA <#IPA> + (in phonemic transcription) that should be matched as + closely as possible (without sounding affected in the target + language) + o Text would be best in either the UTF-8 text, or the code + points in hex HTML. E.g. either of the following: + + "Юникод" + + "Юникод" + + Note: for / supplementary characters/ + , + there should be one hex number per code point, not two + surrogates + : + # 𐀀 /*not*/ �&xDC00; + o If you have a good font, I'd also appreciate a GIF. It + should be *96 x 24* bits, with the text centered, in black + on white (plus grays if smoothed). + * *Other Comments* + o Because some browsers won't handle the text, both text and + GIF image are supplied. If you can’t read the text columns, + see Display Problems + . + o The Chinese versions (inc. Bopomofo) are translations, not + transcriptions, since "transcription in Chinese is pretty + lame" [J. Becker]. + o There are other "translations" of Unicode that may be in + use, such as the Vietnamese "Thống Nhất Mã". + o For sample pages in different languages on the Unicode site, + see What is Unicode? + + o Americans are not generally used to IPA, and find a variety + of different systems in their dictionaries. This one leaves + the base letters as they are, and uses diacritics for + pronunciation. + * *Etymology of /Unicode/* + o Coined by J. Becker. Not related to previous usages, such as: + + A telegraphic code in which one word or set of letters + represents a sentence or phrase; a telegram or message + in this. (late 19th century, OED) + o According to my references, the prefix "uni" is directly + from Latin while the word "code" is through French. + o The original Indo-European apparently would have been + *oino-kau-do ("one strike give"): *kau apparently being + related to such English words as: hew, haggle, hoe, hag, + hay, hack, caudad, caudal, caudate, caudex, coda, codex, + codicil, coward, incus, and Kovač (personal name: "smith"). + + I will leave the exact derivations to the exegetes, + but I like the association with "haggle" myself. + * *Contributions* + o This draws on contributions or comments from: + + Dixon Au + + Joe Becker + + Maurice Bauhahn + + Abel Cheung + + Peter Constable + + Michael Everson + + Christopher John Fynn + + Michael Kaplan + + George Kiraz + + Abdul Malik + + Siva Nataraja + + Roozbeh Pournader + + Jonathan Rosenne + + Jungshik Shin + +------------------------------------------------------------------------ + + +Terms of Use . Last updated: +MED - 04/20/2003 15:30:33. + + + + + +UTF-8 encoded sample plain-text file +‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + +Markus Kuhn [ˈmaʳkʊs kuːn] — 2002-07-25 + + +The ASCII compatible UTF-8 encoding used in this plain-text file +is defined in Unicode, ISO 10646-1, and RFC 2279. + + +Using Unicode/UTF-8, you can write in emails and source code things such as + +Mathematics and sciences: + + ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫ + ⎪⎢⎜│a²+b³ ⎟⎥⎪ + ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β), ⎪⎢⎜│───── ⎟⎥⎪ + ⎪⎢⎜⎷ c₈ ⎟⎥⎪ + ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⎨⎢⎜ ⎟⎥⎬ + ⎪⎢⎜ ∞ ⎟⎥⎪ + ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫), ⎪⎢⎜ ⎲ ⎟⎥⎪ + ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪ + 2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm ⎩⎣⎝i=1 ⎠⎦⎭ + +Linguistics and dictionaries: + + ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn + Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ] + +APL: + + ((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈ + +Nicer typography in plain text files: + + ╔══════════════════════════════════════════╗ + ║ ║ + ║ • ‘single’ and “double” quotes ║ + ║ ║ + ║ • Curly apostrophes: “We’ve been here” ║ + ║ ║ + ║ • Latin-1 apostrophe and accents: '´` ║ + ║ ║ + ║ • ‚deutsche‘ „Anführungszeichen“ ║ + ║ ║ + ║ • †, ‡, ‰, •, 3–4, —, −5/+5, ™, … ║ + ║ ║ + ║ • ASCII safety test: 1lI|, 0OD, 8B ║ + ║ ╭─────────╮ ║ + ║ • the euro symbol: │ 14.95 € │ ║ + ║ ╰─────────╯ ║ + ╚══════════════════════════════════════════╝ + +Combining characters: + + STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑ + +Greek (in Polytonic): + + The Greek anthem: + + Σὲ γνωρίζω ἀπὸ τὴν κόψη + τοῦ σπαθιοῦ τὴν τρομερή, + σὲ γνωρίζω ἀπὸ τὴν ὄψη + ποὺ μὲ βία μετράει τὴ γῆ. + + ᾿Απ᾿ τὰ κόκκαλα βγαλμένη + τῶν ῾Ελλήνων τὰ ἱερά + καὶ σὰν πρῶτα ἀνδρειωμένη + χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά! + + From a speech of Demosthenes in the 4th century BC: + + Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι, + ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς + λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ + τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿ + εἰς τοῦτο προήκοντα, ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ + πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν + οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι, + οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν + ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον + τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι + γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν + προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους + σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ + τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ + τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς + τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον. + + Δημοσθένους, Γ´ ᾿Ολυνθιακὸς + +Georgian: + + From a Unicode conference invitation: + + გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო + კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს, + ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს + ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი, + ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება + ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში, + ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში. + +Russian: + + From a Unicode conference invitation: + + Зарегистрируйтесь сейчас на Десятую Международную Конференцию по + Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии. + Конференция соберет широкий круг экспертов по вопросам глобального + Интернета и Unicode, локализации и интернационализации, воплощению и + применению Unicode в различных операционных системах и программных + приложениях, шрифтах, верстке и многоязычных компьютерных системах. + +Thai (UCS Level 2): + + Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese + classic 'San Gua'): + + [----------------------------|------------------------] + ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่ + สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา + ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา + โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ + เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ + ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ + พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้ + ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ + + (The above is a two-column text. If combining characters are handled + correctly, the lines of the second column should be aligned with the + | character above.) + +Ethiopian: + + Proverbs in the Amharic language: + + ሰማይ አይታረስ ንጉሥ አይከሰስ። + ብላ ካለኝ እንደአባቴ በቆመጠኝ። + ጌጥ ያለቤቱ ቁምጥና ነው። + ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው። + የአፍ ወለምታ በቅቤ አይታሽም። + አይጥ በበላ ዳዋ ተመታ። + ሲተረጉሙ ይደረግሙ። + ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል። + ድር ቢያብር አንበሳ ያስር። + ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም። + እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም። + የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ። + ሥራ ከመፍታት ልጄን ላፋታት። + ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል። + የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ። + ተንጋሎ ቢተፉ ተመልሶ ባፉ። + ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው። + እግርህን በፍራሽህ ልክ ዘርጋ። + +Runes: + + ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ + + (Old English, which transcribed into Latin reads 'He cwaeth that he + bude thaem lande northweardum with tha Westsae.' and means 'He said + that he lived in the northern land near the Western Sea.') + +Braille: + + ⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌ + + ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞ + ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎ + ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂ + ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙ + ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑ + ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲ + + ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ + + ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹ + ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞ + ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕ + ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹ + ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎ + ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎ + ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳ + ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞ + ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ + + (The first couple of paragraphs of "A Christmas Carol" by Dickens) + +Compact font selection example text: + + ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789 + abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ + –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд + ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა + +Greetings in various languages: + + Hello world, Καλημέρα κόσμε, コンニチハ + +Box drawing alignment tests: █ + ▉ + ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳ + ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳ + ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳ + ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳ + ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎ + ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏ + ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█ + ▝▀▘▙▄▟ diff --git a/util/charset/ut/ya.make b/util/charset/ut/ya.make new file mode 100644 index 00000000000..4523035feeb --- /dev/null +++ b/util/charset/ut/ya.make @@ -0,0 +1,14 @@ +UNITTEST_FOR(util/charset) + +DATA(arcadia/util/charset/ut/utf8) + +SRCS( + utf8_ut.cpp + wide_ut.cpp +) + +INCLUDE(${ARCADIA_ROOT}/util/tests/ya_util_tests.inc) + +REQUIREMENTS(ram:17) + +END() diff --git a/util/charset/utf8.cpp b/util/charset/utf8.cpp new file mode 100644 index 00000000000..efe3a52f615 --- /dev/null +++ b/util/charset/utf8.cpp @@ -0,0 +1,170 @@ +#include "unidata.h" +#include "utf8.h" + +namespace { + enum class ECaseConversion { + ToUpper, + ToLower, + }; + + wchar32 ConvertChar(ECaseConversion conversion, wchar32 ch) { + switch (conversion) { + case ECaseConversion::ToUpper: + return ToUpper(ch); + case ECaseConversion::ToLower: + return ToLower(ch); + } + Y_ASSERT(false); // NOTREACHED + return 0; + } + + bool ConvertCaseUTF8Impl(ECaseConversion conversion, const char* beg, size_t n, + TString& newString) { + const unsigned char* p = (const unsigned char*)beg; + const unsigned char* const end = p + n; + + // first loop searches for the first character, which is changed by ConvertChar + // if there is no changed character, we don't need reallocation/copy + wchar32 cNew = 0; + size_t cLen = 0; + while (p < end) { + wchar32 c; + if (RECODE_OK != SafeReadUTF8Char(c, cLen, p, end)) { + ythrow yexception() + << "failed to decode UTF-8 string at pos " << ((const char*)p - beg); + } + cNew = ConvertChar(conversion, c); + + if (cNew != c) + break; + p += cLen; + } + if (p == end) { + return false; + } + + // some character changed after ToLower. Write new string to newString. + newString.resize(n); + + size_t written = (char*)p - beg; + char* writePtr = newString.begin(); + memcpy(writePtr, beg, written); + writePtr += written; + size_t destSpace = n - written; + + // before each iteration (including the first one) variable 'cNew' contains unwritten symbol + while (true) { + size_t cNewLen; + Y_ASSERT((writePtr - newString.data()) + destSpace == newString.size()); + if (RECODE_EOOUTPUT == + SafeWriteUTF8Char(cNew, cNewLen, (unsigned char*)writePtr, destSpace)) { + destSpace += newString.size(); + newString.resize(newString.size() * 2); + writePtr = newString.begin() + (newString.size() - destSpace); + continue; + } + destSpace -= cNewLen; + writePtr += cNewLen; + p += cLen; + if (p == end) { + newString.resize(newString.size() - destSpace); + return true; + } + wchar32 c = 0; + if (RECODE_OK != SafeReadUTF8Char(c, cLen, p, end)) { + ythrow yexception() + << "failed to decode UTF-8 string at pos " << ((const char*)p - beg); + } + cNew = ConvertChar(conversion, c); + } + Y_ASSERT(false); + return false; + } +} // namespace + +extern const wchar32 BROKEN_RUNE = 0xFFFD; + +static const char* SkipUTF8Chars(const char* begin, const char* end, size_t numChars) { + const unsigned char* uEnd = reinterpret_cast(end); + while (begin != end && numChars > 0) { + const unsigned char* uBegin = reinterpret_cast(begin); + size_t runeLen; + if (GetUTF8CharLen(runeLen, uBegin, uEnd) != RECODE_OK) { + ythrow yexception() << "invalid UTF-8 char"; + } + begin += runeLen; + Y_ASSERT(begin <= end); + --numChars; + } + return begin; +} + +TStringBuf SubstrUTF8(const TStringBuf str, size_t pos, size_t len) { + const char* start = SkipUTF8Chars(str.begin(), str.end(), pos); + const char* end = SkipUTF8Chars(start, str.end(), len); + return TStringBuf(start, end - start); +} + +EUTF8Detect UTF8Detect(const char* s, size_t len) { + const unsigned char* s0 = (const unsigned char*)s; + const unsigned char* send = s0 + len; + wchar32 rune; + size_t rune_len; + EUTF8Detect res = ASCII; + + while (s0 < send) { + RECODE_RESULT rr = SafeReadUTF8Char(rune, rune_len, s0, send); + + if (rr != RECODE_OK) { + return NotUTF8; + } + + if (rune_len > 1) { + res = UTF8; + } + + s0 += rune_len; + } + + return res; +} + +bool ToLowerUTF8Impl(const char* beg, size_t n, TString& newString) { + return ConvertCaseUTF8Impl(ECaseConversion::ToLower, beg, n, newString); +} + +TString ToLowerUTF8(const TString& s) { + TString newString; + bool changed = ToLowerUTF8Impl(s.data(), s.size(), newString); + return changed ? newString : s; +} + +TString ToLowerUTF8(TStringBuf s) { + TString newString; + bool changed = ToLowerUTF8Impl(s.data(), s.size(), newString); + return changed ? newString : TString(s.data(), s.size()); +} + +TString ToLowerUTF8(const char* s) { + return ToLowerUTF8(TStringBuf(s)); +} + +bool ToUpperUTF8Impl(const char* beg, size_t n, TString& newString) { + return ConvertCaseUTF8Impl(ECaseConversion::ToUpper, beg, n, newString); +} + +TString ToUpperUTF8(const TString& s) { + TString newString; + bool changed = ToUpperUTF8Impl(s.data(), s.size(), newString); + return changed ? newString : s; +} + +TString ToUpperUTF8(TStringBuf s) { + TString newString; + bool changed = ToUpperUTF8Impl(s.data(), s.size(), newString); + return changed ? newString : TString(s.data(), s.size()); +} + +TString ToUpperUTF8(const char* s) { + return ToUpperUTF8(TStringBuf(s)); +} diff --git a/util/charset/utf8.h b/util/charset/utf8.h new file mode 100644 index 00000000000..76c1f94078a --- /dev/null +++ b/util/charset/utf8.h @@ -0,0 +1,431 @@ +#pragma once + +#include "recode_result.h" + +#include +#include +#include +#include +#include + +extern const wchar32 BROKEN_RUNE; + +inline unsigned char UTF8LeadByteMask(size_t utf8_rune_len) { + // Y_ASSERT (utf8_rune_len <= 4); + return "\0\0\037\017\007"[utf8_rune_len]; +} + +inline size_t UTF8RuneLen(const unsigned char lead_byte) { + //b0XXXXXXX + if ((lead_byte & 0x80) == 0x00) { + return 1; + } + //b110XXXXX + if ((lead_byte & 0xe0) == 0xc0) { + return 2; + } + //b1110XXXX + if ((lead_byte & 0xf0) == 0xe0) { + return 3; + } + //b11110XXX + if ((lead_byte & 0xf8) == 0xf0) { + return 4; + } + //b10XXXXXX + return 0; +} + +inline size_t UTF8RuneLenByUCS(wchar32 rune) { + if (rune < 0x80) + return 1U; + else if (rune < 0x800) + return 2U; + else if (rune < 0x10000) + return 3U; + else if (rune < 0x200000) + return 4U; + else if (rune < 0x4000000) + return 5U; + else + return 6U; +} + +inline void PutUTF8LeadBits(wchar32& rune, unsigned char c, size_t len) { + rune = c; + rune &= UTF8LeadByteMask(len); +} + +inline void PutUTF8SixBits(wchar32& rune, unsigned char c) { + rune <<= 6; + rune |= c & 0x3F; +} + +inline bool IsUTF8ContinuationByte(unsigned char c) { + return (c & static_cast(0xC0)) == static_cast(0x80); +} + +//! returns length of the current UTF8 character +//! @param n length of the current character, it is assigned in case of valid UTF8 byte sequence +//! @param p pointer to the current character +//! @param e end of the character sequence +inline RECODE_RESULT GetUTF8CharLen(size_t& n, const unsigned char* p, const unsigned char* e) { + Y_ASSERT(p < e); // since p < e then we will check RECODE_EOINPUT only for n > 1 (see calls of this functions) + switch (UTF8RuneLen(*p)) { + case 0: + return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in first byte + + case 1: + n = 1; + return RECODE_OK; + + case 2: + if (p + 2 > e) { + return RECODE_EOINPUT; + } else if (!IsUTF8ContinuationByte(p[1])) { + return RECODE_BROKENSYMBOL; + } else { + n = 2; + return RECODE_OK; + } + case 3: + if (p + 3 > e) { + return RECODE_EOINPUT; + } else if (!IsUTF8ContinuationByte(p[1]) || !IsUTF8ContinuationByte(p[2])) { + return RECODE_BROKENSYMBOL; + } else { + n = 3; + return RECODE_OK; + } + default: // actually 4 + if (p + 4 > e) { + return RECODE_EOINPUT; + } else if (!IsUTF8ContinuationByte(p[1]) || !IsUTF8ContinuationByte(p[2]) || !IsUTF8ContinuationByte(p[3])) { + return RECODE_BROKENSYMBOL; + } else { + n = 4; + return RECODE_OK; + } + } +} + +//! returns number of characters in UTF8 encoded text, stops immediately if UTF8 byte sequence is wrong +//! @param text UTF8 encoded text +//! @param len the length of the text in bytes +//! @param number number of encoded symbols in the text +inline bool GetNumberOfUTF8Chars(const char* text, size_t len, size_t& number) { + const unsigned char* cur = reinterpret_cast(text); + const unsigned char* const last = cur + len; + number = 0; + size_t runeLen; + bool res = true; + while (cur != last) { + if (GetUTF8CharLen(runeLen, cur, last) != RECODE_OK) { // actually it could be RECODE_BROKENSYMBOL only + res = false; + break; + } + cur += runeLen; + Y_ASSERT(cur <= last); + ++number; + } + return res; +} + +inline size_t GetNumberOfUTF8Chars(TStringBuf text) { + size_t number; + if (!GetNumberOfUTF8Chars(text.data(), text.size(), number)) { + ythrow yexception() << "GetNumberOfUTF8Chars failed on invalid utf-8 " << TString(text.substr(0, 50)).Quote(); + } + return number; +} + +enum class StrictUTF8 { + Yes, + No +}; + +template +inline bool IsValidUTF8Rune(wchar32 rune); + +template <> +inline bool IsValidUTF8Rune<2, StrictUTF8::Yes>(wchar32 rune) { + // check for overlong encoding + return rune >= 0x80; +} + +template <> +inline bool IsValidUTF8Rune<2, StrictUTF8::No>(wchar32 rune) { + return IsValidUTF8Rune<2, StrictUTF8::Yes>(rune); +} + +template <> +inline bool IsValidUTF8Rune<3, StrictUTF8::Yes>(wchar32 rune) { + // surrogates are forbidden by RFC3629 section 3 + return rune >= 0x800 && (rune < 0xD800 || rune > 0xDFFF); +} + +template <> +inline bool IsValidUTF8Rune<3, StrictUTF8::No>(wchar32 rune) { + // check for overlong encoding + return rune >= 0x800; +} + +template <> +inline bool IsValidUTF8Rune<4, StrictUTF8::Yes>(wchar32 rune) { + // check if this is a valid sumbod without overlong encoding + return rune <= 0x10FFFF && rune >= 0x10000; +} + +template <> +inline bool IsValidUTF8Rune<4, StrictUTF8::No>(wchar32 rune) { + return IsValidUTF8Rune<4, StrictUTF8::Yes>(rune); +} + +//! reads one unicode symbol from a character sequence encoded UTF8 and checks for overlong encoding +//! @param rune value of the current character +//! @param rune_len length of the UTF8 bytes sequence that has been read +//! @param s pointer to the current character +//! @param end the end of the character sequence +template +inline RECODE_RESULT SafeReadUTF8Char(wchar32& rune, size_t& rune_len, const unsigned char* s, const unsigned char* end) { + rune = BROKEN_RUNE; + rune_len = 0; + wchar32 _rune; + + size_t _len = UTF8RuneLen(*s); + if (s + _len > end) + return RECODE_EOINPUT; //[EOINPUT] + if (_len == 0) + return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in first byte + _rune = *s++; //[00000000 0XXXXXXX] + + if (_len > 1) { + _rune &= UTF8LeadByteMask(_len); + unsigned char ch = *s++; + if (!IsUTF8ContinuationByte(ch)) + return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in second byte + PutUTF8SixBits(_rune, ch); //[00000XXX XXYYYYYY] + if (_len > 2) { + ch = *s++; + if (!IsUTF8ContinuationByte(ch)) + return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in third byte + PutUTF8SixBits(_rune, ch); //[XXXXYYYY YYZZZZZZ] + if (_len > 3) { + ch = *s; + if (!IsUTF8ContinuationByte(ch)) + return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in fourth byte + PutUTF8SixBits(_rune, ch); //[XXXYY YYYYZZZZ ZZQQQQQQ] + if (!IsValidUTF8Rune<4, strictMode>(_rune)) + return RECODE_BROKENSYMBOL; + } else { + if (!IsValidUTF8Rune<3, strictMode>(_rune)) + return RECODE_BROKENSYMBOL; + } + } else { + if (!IsValidUTF8Rune<2, strictMode>(_rune)) + return RECODE_BROKENSYMBOL; + } + } + rune_len = _len; + rune = _rune; + return RECODE_OK; +} + +//! reads one unicode symbol from a character sequence encoded UTF8 and moves pointer to the next character +//! @param c value of the current character +//! @param p pointer to the current character, it will be changed in case of valid UTF8 byte sequence +//! @param e the end of the character sequence +template +Y_FORCE_INLINE RECODE_RESULT ReadUTF8CharAndAdvance(wchar32& rune, const unsigned char*& p, const unsigned char* e) noexcept { + Y_ASSERT(p < e); // since p < e then we will check RECODE_EOINPUT only for n > 1 (see calls of this functions) + switch (UTF8RuneLen(*p)) { + case 0: + rune = BROKEN_RUNE; + return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in first byte + + case 1: + rune = *p; //[00000000 0XXXXXXX] + ++p; + return RECODE_OK; + + case 2: + if (p + 2 > e) { + return RECODE_EOINPUT; + } else if (!IsUTF8ContinuationByte(p[1])) { + rune = BROKEN_RUNE; + return RECODE_BROKENSYMBOL; + } else { + PutUTF8LeadBits(rune, *p++, 2); //[00000000 000XXXXX] + PutUTF8SixBits(rune, *p++); //[00000XXX XXYYYYYY] + if (!IsValidUTF8Rune<2, strictMode>(rune)) { + p -= 2; + rune = BROKEN_RUNE; + return RECODE_BROKENSYMBOL; + } + return RECODE_OK; + } + case 3: + if (p + 3 > e) { + return RECODE_EOINPUT; + } else if (!IsUTF8ContinuationByte(p[1]) || !IsUTF8ContinuationByte(p[2])) { + rune = BROKEN_RUNE; + return RECODE_BROKENSYMBOL; + } else { + PutUTF8LeadBits(rune, *p++, 3); //[00000000 0000XXXX] + PutUTF8SixBits(rune, *p++); //[000000XX XXYYYYYY] + PutUTF8SixBits(rune, *p++); //[XXXXYYYY YYZZZZZZ] + // check for overlong encoding and surrogates + if (!IsValidUTF8Rune<3, strictMode>(rune)) { + p -= 3; + rune = BROKEN_RUNE; + return RECODE_BROKENSYMBOL; + } + return RECODE_OK; + } + case 4: + if (p + 4 > e) { + return RECODE_EOINPUT; + } else if (!IsUTF8ContinuationByte(p[1]) || !IsUTF8ContinuationByte(p[2]) || !IsUTF8ContinuationByte(p[3])) { + rune = BROKEN_RUNE; + return RECODE_BROKENSYMBOL; + } else { + PutUTF8LeadBits(rune, *p++, 4); //[00000000 00000000 00000XXX] + PutUTF8SixBits(rune, *p++); //[00000000 0000000X XXYYYYYY] + PutUTF8SixBits(rune, *p++); //[00000000 0XXXYYYY YYZZZZZZ] + PutUTF8SixBits(rune, *p++); //[000XXXYY YYYYZZZZ ZZQQQQQQ] + if (!IsValidUTF8Rune<4, strictMode>(rune)) { + p -= 4; + rune = BROKEN_RUNE; + return RECODE_BROKENSYMBOL; + } + return RECODE_OK; + } + default: // >4 + rune = BROKEN_RUNE; + return RECODE_BROKENSYMBOL; + } +} + +//! writes one unicode symbol into a character sequence encoded UTF8 +//! checks for end of the buffer and returns the result of encoding +//! @param rune value of the current character +//! @param rune_len length of the UTF8 byte sequence that has been written +//! @param s pointer to the output buffer +//! @param tail available size of the buffer +inline RECODE_RESULT SafeWriteUTF8Char(wchar32 rune, size_t& rune_len, unsigned char* s, size_t tail) { + rune_len = 0; + if (rune < 0x80) { + if (tail <= 0) + return RECODE_EOOUTPUT; + *s = static_cast(rune); + rune_len = 1; + return RECODE_OK; + } + if (rune < 0x800) { + if (tail <= 1) + return RECODE_EOOUTPUT; + *s++ = static_cast(0xC0 | (rune >> 6)); + *s = static_cast(0x80 | (rune & 0x3F)); + rune_len = 2; + return RECODE_OK; + } + if (rune < 0x10000) { + if (tail <= 2) + return RECODE_EOOUTPUT; + *s++ = static_cast(0xE0 | (rune >> 12)); + *s++ = static_cast(0x80 | ((rune >> 6) & 0x3F)); + *s = static_cast(0x80 | (rune & 0x3F)); + rune_len = 3; + return RECODE_OK; + } + /*if (rune < 0x200000)*/ { + if (tail <= 3) + return RECODE_EOOUTPUT; + *s++ = static_cast(0xF0 | ((rune >> 18) & 0x07)); + *s++ = static_cast(0x80 | ((rune >> 12) & 0x3F)); + *s++ = static_cast(0x80 | ((rune >> 6) & 0x3F)); + *s = static_cast(0x80 | (rune & 0x3F)); + rune_len = 4; + return RECODE_OK; + } +} + +inline RECODE_RESULT SafeWriteUTF8Char(wchar32 rune, size_t& rune_len, unsigned char* s, const unsigned char* end) { + return SafeWriteUTF8Char(rune, rune_len, s, end - s); +} + +//! writes one unicode symbol into a character sequence encoded UTF8 +//! @attention this function works as @c SafeWriteUTF8Char it does not check +//! the size of the output buffer, it supposes that buffer is long enough +//! @param rune value of the current character +//! @param rune_len length of the UTF8 byte sequence that has been written +//! @param s pointer to the output buffer +inline void WriteUTF8Char(wchar32 rune, size_t& rune_len, unsigned char* s) { + if (rune < 0x80) { + *s = static_cast(rune); + rune_len = 1; + return; + } + if (rune < 0x800) { + *s++ = static_cast(0xC0 | (rune >> 6)); + *s = static_cast(0x80 | (rune & 0x3F)); + rune_len = 2; + return; + } + if (rune < 0x10000) { + *s++ = static_cast(0xE0 | (rune >> 12)); + *s++ = static_cast(0x80 | ((rune >> 6) & 0x3F)); + *s = static_cast(0x80 | (rune & 0x3F)); + rune_len = 3; + return; + } + /*if (rune < 0x200000)*/ { + *s++ = static_cast(0xF0 | ((rune >> 18) & 0x07)); + *s++ = static_cast(0x80 | ((rune >> 12) & 0x3F)); + *s++ = static_cast(0x80 | ((rune >> 6) & 0x3F)); + *s = static_cast(0x80 | (rune & 0x3F)); + rune_len = 4; + } +} + +TStringBuf SubstrUTF8(const TStringBuf str, size_t pos, size_t len); + +enum EUTF8Detect { + NotUTF8, + UTF8, + ASCII +}; + +EUTF8Detect UTF8Detect(const char* s, size_t len); + +inline EUTF8Detect UTF8Detect(const TStringBuf input) { + return UTF8Detect(input.data(), input.size()); +} + +inline bool IsUtf(const char* input, size_t len) { + return UTF8Detect(input, len) != NotUTF8; +} + +inline bool IsUtf(const TStringBuf input) { + return IsUtf(input.data(), input.size()); +} + +//! returns true, if result is not the same as input, and put it in newString +//! returns false, if result is unmodified +bool ToLowerUTF8Impl(const char* beg, size_t n, TString& newString); + +TString ToLowerUTF8(const TString& s); +TString ToLowerUTF8(TStringBuf s); +TString ToLowerUTF8(const char* s); + +inline TString ToLowerUTF8(const std::string& s) { + return ToLowerUTF8(TStringBuf(s)); +} + +//! returns true, if result is not the same as input, and put it in newString +//! returns false, if result is unmodified +bool ToUpperUTF8Impl(const char* beg, size_t n, TString& newString); + +TString ToUpperUTF8(const TString& s); +TString ToUpperUTF8(TStringBuf s); +TString ToUpperUTF8(const char* s); diff --git a/util/charset/utf8_ut.cpp b/util/charset/utf8_ut.cpp new file mode 100644 index 00000000000..9e68881cca2 --- /dev/null +++ b/util/charset/utf8_ut.cpp @@ -0,0 +1,126 @@ +#include "utf8.h" +#include "wide.h" + +#include +#include + +#include +#include + +Y_UNIT_TEST_SUITE(TUtfUtilTest) { + Y_UNIT_TEST(TestUTF8Len) { + UNIT_ASSERT_EQUAL(GetNumberOfUTF8Chars("привет!"), 7); + } + + Y_UNIT_TEST(TestToLowerUtfString) { + UNIT_ASSERT_VALUES_EQUAL(ToLowerUTF8("xyz XYZ ПРИВЕТ!"), "xyz xyz привет!"); + + UNIT_ASSERT_VALUES_EQUAL(ToLowerUTF8(TStringBuf("xyz")), "xyz"); + + { + TString s = "привет!"; + TString q = "ПРИВЕТ!"; + TString tmp; + UNIT_ASSERT(ToLowerUTF8Impl(s.data(), s.size(), tmp) == false); + UNIT_ASSERT(ToLowerUTF8Impl(q.data(), q.size(), tmp) == true); + } + + { + const char* weird = "\xC8\xBE"; // 'Ⱦ', U+023E. strlen(weird)==2, strlen(tolower_utf8(weird)) is 3 + const char* turkI = "İ"; //strlen("İ") == 2, strlen(tolower_utf8("İ") == 1 + TStringBuf chars[] = {"f", "F", "Б", "б", weird, turkI}; + const int N = Y_ARRAY_SIZE(chars); + //try all combinations of these letters. + int numberOfVariants = 1; + for (int len = 0; len <= 4; ++len) { + for (int i = 0; i < numberOfVariants; ++i) { + TString s; + int k = i; + for (int j = 0; j < len; ++j) { + //Treat 'i' like number in base-N system with digits from 'chars'-array + s += chars[k % N]; + k /= N; + } + + TUtf16String tmp = UTF8ToWide(s); + tmp.to_lower(); + + UNIT_ASSERT_VALUES_EQUAL(ToLowerUTF8(s), WideToUTF8(tmp)); + } + numberOfVariants *= N; + } + } + } + + Y_UNIT_TEST(TestToUpperUtfString) { + UNIT_ASSERT_VALUES_EQUAL(ToUpperUTF8("xyz XYZ привет!"), "XYZ XYZ ПРИВЕТ!"); + + UNIT_ASSERT_VALUES_EQUAL(ToUpperUTF8(TStringBuf("XYZ")), "XYZ"); + + { + TString s = "ПРИВЕТ!"; + TString q = "привет!"; + TString tmp; + UNIT_ASSERT(ToUpperUTF8Impl(s.data(), s.size(), tmp) == false); + UNIT_ASSERT(ToUpperUTF8Impl(q.data(), q.size(), tmp) == true); + } + + { + const char* weird = "\xC8\xBE"; // 'Ⱦ', U+023E. strlen(weird)==2, strlen(ToUpper_utf8(weird)) is 3 + const char* turkI = "İ"; //strlen("İ") == 2, strlen(ToUpper_utf8("İ") == 1 + TStringBuf chars[] = {"F", "f", "б", "Б", turkI, weird}; + const int N = Y_ARRAY_SIZE(chars); + //try all combinations of these letters. + int numberOfVariants = 1; + for (int len = 0; len <= 4; ++len) { + for (int i = 0; i < numberOfVariants; ++i) { + TString s; + int k = i; + for (int j = 0; j < len; ++j) { + //Treat 'i' like number in base-N system with digits from 'chars'-array + s += chars[k % N]; + k /= N; + } + + TUtf16String tmp = UTF8ToWide(s); + tmp.to_upper(); + + UNIT_ASSERT_VALUES_EQUAL(ToUpperUTF8(s), WideToUTF8(tmp)); + } + numberOfVariants *= N; + } + } + } + + Y_UNIT_TEST(TestUTF8ToWide) { + TFileInput in(ArcadiaSourceRoot() + TStringBuf("/util/charset/ut/utf8/test1.txt")); + + TString text = in.ReadAll(); + UNIT_ASSERT(WideToUTF8(UTF8ToWide(text)) == text); + } + + Y_UNIT_TEST(TestInvalidUTF8) { + TVector testData; + TFileInput input(ArcadiaSourceRoot() + TStringBuf("/util/charset/ut/utf8/invalid_UTF8.bin")); + Load(&input, testData); + + for (const auto& text : testData) { + UNIT_ASSERT_EXCEPTION(UTF8ToWide(text), yexception); + } + } + + Y_UNIT_TEST(TestUTF8ToWideScalar) { + TFileInput in(ArcadiaSourceRoot() + TStringBuf("/util/charset/ut/utf8/test1.txt")); + + TString text = in.ReadAll(); + TUtf16String wtextSSE = UTF8ToWide(text); + TUtf16String wtextScalar = TUtf16String::Uninitialized(text.size()); + const unsigned char* textBegin = reinterpret_cast(text.c_str()); + wchar16* wtextBegin = wtextScalar.begin(); + ::NDetail::UTF8ToWideImplScalar(textBegin, textBegin + text.size(), wtextBegin); + UNIT_ASSERT(wtextBegin == wtextScalar.begin() + wtextSSE.size()); + UNIT_ASSERT(textBegin == reinterpret_cast(text.end())); + wtextScalar.remove(wtextSSE.size()); + UNIT_ASSERT(wtextScalar == wtextSSE); + } +} diff --git a/util/charset/wide.cpp b/util/charset/wide.cpp new file mode 100644 index 00000000000..a287438ddde --- /dev/null +++ b/util/charset/wide.cpp @@ -0,0 +1,626 @@ +#include "wide.h" + +#include +#include + +namespace { + //! the constants are not zero-terminated + const wchar16 LT[] = {'&', 'l', 't', ';'}; + const wchar16 GT[] = {'&', 'g', 't', ';'}; + const wchar16 AMP[] = {'&', 'a', 'm', 'p', ';'}; + const wchar16 BR[] = {'<', 'B', 'R', '>'}; + const wchar16 QUOT[] = {'&', 'q', 'u', 'o', 't', ';'}; + + template + inline size_t EscapedLen(wchar16 c) { + switch (c) { + case '<': + return Y_ARRAY_SIZE(LT); + case '>': + return Y_ARRAY_SIZE(GT); + case '&': + return Y_ARRAY_SIZE(AMP); + case '\"': + return Y_ARRAY_SIZE(QUOT); + default: + if (insertBr && (c == '\r' || c == '\n')) + return Y_ARRAY_SIZE(BR); + else + return 1; + } + } +} + +void Collapse(TUtf16String& w) { + CollapseImpl(w, w, 0, IsWhitespace); +} + +size_t Collapse(wchar16* s, size_t n) { + return CollapseImpl(s, n, IsWhitespace); +} + +TWtringBuf StripLeft(const TWtringBuf text) noexcept { + const auto* p = text.data(); + const auto* const pe = text.data() + text.size(); + + for (; p != pe && IsWhitespace(*p); ++p) { + } + + return {p, pe}; +} + +void StripLeft(TUtf16String& text) { + const auto stripped = StripLeft(TWtringBuf(text)); + if (stripped.size() == text.size()) { + return; + } + + text = stripped; +} + +TWtringBuf StripRight(const TWtringBuf text) noexcept { + if (!text) { + return {}; + } + + const auto* const pe = text.data() - 1; + const auto* p = text.data() + text.size() - 1; + + for (; p != pe && IsWhitespace(*p); --p) { + } + + return {pe + 1, p + 1}; +} + +void StripRight(TUtf16String& text) { + const auto stripped = StripRight(TWtringBuf(text)); + if (stripped.size() == text.size()) { + return; + } + + text.resize(stripped.size()); +} + +TWtringBuf Strip(const TWtringBuf text) noexcept { + return StripRight(StripLeft(text)); +} + +void Strip(TUtf16String& text) { + StripLeft(text); + StripRight(text); +} + +template +static bool IsReductionOnSymbolsTrue(const TWtringBuf text, T&& f) { + const auto* p = text.data(); + const auto* const pe = text.data() + text.length(); + while (p != pe) { + const auto symbol = ReadSymbolAndAdvance(p, pe); + if (!f(symbol)) { + return false; + } + } + + return true; +} + +bool IsLowerWord(const TWtringBuf text) noexcept { + return IsReductionOnSymbolsTrue(text, [](const wchar32 s) { return IsLower(s); }); +} + +bool IsUpperWord(const TWtringBuf text) noexcept { + return IsReductionOnSymbolsTrue(text, [](const wchar32 s) { return IsUpper(s); }); +} + +bool IsLower(const TWtringBuf text) noexcept { + return IsReductionOnSymbolsTrue(text, [](const wchar32 s) { + if (IsAlpha(s)) { + return IsLower(s); + } + return true; + }); +} + +bool IsUpper(const TWtringBuf text) noexcept { + return IsReductionOnSymbolsTrue(text, [](const wchar32 s) { + if (IsAlpha(s)) { + return IsUpper(s); + } + return true; + }); +} + +bool IsTitleWord(const TWtringBuf text) noexcept { + if (!text) { + return false; + } + + const auto* p = text.data(); + const auto* pe = text.data() + text.size(); + + const auto firstSymbol = ReadSymbolAndAdvance(p, pe); + if (firstSymbol != ToTitle(firstSymbol)) { + return false; + } + + return IsLowerWord({p, pe}); +} + +template +static bool ModifySequence(TCharType*& p, const TCharType* const pe, F&& f) { + while (p != pe) { + const auto symbol = ReadSymbol(p, pe); + const auto modified = f(symbol); + if (symbol != modified) { + if (stopOnFirstModification) { + return true; + } + + WriteSymbol(modified, p); // also moves `p` forward + } else { + p = SkipSymbol(p, pe); + } + } + + return false; +} + +template +static bool ModifySequence(const TCharType*& p, const TCharType* const pe, TCharType*& out, F&& f) { + while (p != pe) { + const auto symbol = stopOnFirstModification ? ReadSymbol(p, pe) : ReadSymbolAndAdvance(p, pe); + const auto modified = f(symbol); + + if (stopOnFirstModification) { + if (symbol != modified) { + return true; + } + + p = SkipSymbol(p, pe); + } + + WriteSymbol(modified, out); + } + + return false; +} + +template +static void DetachAndFixPointers(TStringType& text, typename TStringType::value_type*& p, const typename TStringType::value_type*& pe) { + const auto pos = p - text.data(); + const auto count = pe - p; + p = text.Detach() + pos; + pe = p + count; +} + +template +static bool ModifyStringSymbolwise(TStringType& text, size_t pos, size_t count, F&& f) { + // TODO(yazevnul): this is done for consistency with `TUtf16String::to_lower` and friends + // at r2914050, maybe worth replacing them with asserts. Also see the same code in `ToTitle`. + pos = pos < text.size() ? pos : text.size(); + count = count < text.size() - pos ? count : text.size() - pos; + + // TUtf16String is refcounted and it's `data` method return pointer to the constant memory. + // To simplify the code we do a `const_cast`, though first write to the memory will be done only + // after we call `Detach()` and get pointer to a writable piece of memory. + auto* p = const_cast(text.data() + pos); + const auto* pe = text.data() + pos + count; + + if (ModifySequence(p, pe, f)) { + DetachAndFixPointers(text, p, pe); + ModifySequence(p, pe, f); + return true; + } + + return false; +} + +bool ToLower(TUtf16String& text, size_t pos, size_t count) { + const auto f = [](const wchar32 s) { return ToLower(s); }; + return ModifyStringSymbolwise(text, pos, count, f); +} + +bool ToUpper(TUtf16String& text, size_t pos, size_t count) { + const auto f = [](const wchar32 s) { return ToUpper(s); }; + return ModifyStringSymbolwise(text, pos, count, f); +} + +bool ToLower(TUtf32String& text, size_t pos, size_t count) { + const auto f = [](const wchar32 s) { return ToLower(s); }; + return ModifyStringSymbolwise(text, pos, count, f); +} + +bool ToUpper(TUtf32String& text, size_t pos, size_t count) { + const auto f = [](const wchar32 s) { return ToUpper(s); }; + return ModifyStringSymbolwise(text, pos, count, f); +} + +bool ToTitle(TUtf16String& text, size_t pos, size_t count) { + if (!text) { + return false; + } + + pos = pos < text.size() ? pos : text.size(); + count = count < text.size() - pos ? count : text.size() - pos; + + const auto toLower = [](const wchar32 s) { return ToLower(s); }; + + auto* p = const_cast(text.data() + pos); + const auto* pe = text.data() + pos + count; + + const auto firstSymbol = ReadSymbol(p, pe); + if (firstSymbol == ToTitle(firstSymbol)) { + p = SkipSymbol(p, pe); + if (ModifySequence(p, pe, toLower)) { + DetachAndFixPointers(text, p, pe); + ModifySequence(p, pe, toLower); + return true; + } + } else { + DetachAndFixPointers(text, p, pe); + WriteSymbol(ToTitle(ReadSymbol(p, pe)), p); // also moves `p` forward + ModifySequence(p, pe, toLower); + return true; + } + + return false; +} + +bool ToTitle(TUtf32String& text, size_t pos, size_t count) { + if (!text) { + return false; + } + + pos = pos < text.size() ? pos : text.size(); + count = count < text.size() - pos ? count : text.size() - pos; + + const auto toLower = [](const wchar32 s) { return ToLower(s); }; + + auto* p = const_cast(text.data() + pos); + const auto* pe = text.data() + pos + count; + + const auto firstSymbol = *p; + if (firstSymbol == ToTitle(firstSymbol)) { + p += 1; + if (ModifySequence(p, pe, toLower)) { + DetachAndFixPointers(text, p, pe); + ModifySequence(p, pe, toLower); + return true; + } + } else { + DetachAndFixPointers(text, p, pe); + WriteSymbol(ToTitle(ReadSymbol(p, pe)), p); // also moves `p` forward + ModifySequence(p, pe, toLower); + return true; + } + + return false; +} + +TUtf16String ToLowerRet(TUtf16String text, size_t pos, size_t count) { + ToLower(text, pos, count); + return text; +} + +TUtf16String ToUpperRet(TUtf16String text, size_t pos, size_t count) { + ToUpper(text, pos, count); + return text; +} + +TUtf16String ToTitleRet(TUtf16String text, size_t pos, size_t count) { + ToTitle(text, pos, count); + return text; +} + +TUtf32String ToLowerRet(TUtf32String text, size_t pos, size_t count) { + ToLower(text, pos, count); + return text; +} + +TUtf32String ToUpperRet(TUtf32String text, size_t pos, size_t count) { + ToUpper(text, pos, count); + return text; +} + +TUtf32String ToTitleRet(TUtf32String text, size_t pos, size_t count) { + ToTitle(text, pos, count); + return text; +} + +bool ToLower(const wchar16* text, size_t length, wchar16* out) noexcept { + // TODO(yazevnul): get rid of `text == out` case (it is probably used only in lemmer) and then + // we can declare text and out as `__restrict__` + Y_ASSERT(text == out || !(out >= text && out < text + length)); + const auto f = [](const wchar32 s) { return ToLower(s); }; + const auto* p = text; + const auto* const pe = text + length; + if (ModifySequence(p, pe, out, f)) { + ModifySequence(p, pe, out, f); + return true; + } + return false; +} + +bool ToUpper(const wchar16* text, size_t length, wchar16* out) noexcept { + Y_ASSERT(text == out || !(out >= text && out < text + length)); + const auto f = [](const wchar32 s) { return ToUpper(s); }; + const auto* p = text; + const auto* const pe = text + length; + if (ModifySequence(p, pe, out, f)) { + ModifySequence(p, pe, out, f); + return true; + } + return false; +} + +bool ToTitle(const wchar16* text, size_t length, wchar16* out) noexcept { + if (!length) { + return false; + } + + Y_ASSERT(text == out || !(out >= text && out < text + length)); + + const auto* const textEnd = text + length; + const auto firstSymbol = ReadSymbolAndAdvance(text, textEnd); + const auto firstSymbolTitle = ToTitle(firstSymbol); + + WriteSymbol(firstSymbolTitle, out); + + return ToLower(text, textEnd - text, out) || firstSymbol != firstSymbolTitle; +} + +bool ToLower(wchar16* text, size_t length) noexcept { + const auto f = [](const wchar32 s) { return ToLower(s); }; + const auto* const textEnd = text + length; + if (ModifySequence(text, textEnd, f)) { + ModifySequence(text, textEnd, f); + return true; + } + return false; +} + +bool ToUpper(wchar16* text, size_t length) noexcept { + const auto f = [](const wchar32 s) { return ToUpper(s); }; + const auto* const textEnd = text + length; + if (ModifySequence(text, textEnd, f)) { + ModifySequence(text, textEnd, f); + return true; + } + return false; +} + +bool ToTitle(wchar16* text, size_t length) noexcept { + if (!length) { + return false; + } + + const auto* textEnd = text + length; + const auto firstSymbol = ReadSymbol(text, textEnd); + const auto firstSymbolTitle = ToTitle(firstSymbol); + + // avoid unnacessary writes to the memory + if (firstSymbol != firstSymbolTitle) { + WriteSymbol(firstSymbolTitle, text); + } else { + text = SkipSymbol(text, textEnd); + } + + return ToLower(text, textEnd - text) || firstSymbol != firstSymbolTitle; +} + +bool ToLower(const wchar32* text, size_t length, wchar32* out) noexcept { + // TODO(yazevnul): get rid of `text == out` case (it is probably used only in lemmer) and then + // we can declare text and out as `__restrict__` + Y_ASSERT(text == out || !(out >= text && out < text + length)); + const auto f = [](const wchar32 s) { return ToLower(s); }; + const auto* p = text; + const auto* const pe = text + length; + if (ModifySequence(p, pe, out, f)) { + ModifySequence(p, pe, out, f); + return true; + } + return false; +} + +bool ToUpper(const wchar32* text, size_t length, wchar32* out) noexcept { + Y_ASSERT(text == out || !(out >= text && out < text + length)); + const auto f = [](const wchar32 s) { return ToUpper(s); }; + const auto* p = text; + const auto* const pe = text + length; + if (ModifySequence(p, pe, out, f)) { + ModifySequence(p, pe, out, f); + return true; + } + return false; +} + +bool ToTitle(const wchar32* text, size_t length, wchar32* out) noexcept { + if (!length) { + return false; + } + + Y_ASSERT(text == out || !(out >= text && out < text + length)); + + const auto* const textEnd = text + length; + const auto firstSymbol = ReadSymbolAndAdvance(text, textEnd); + const auto firstSymbolTitle = ToTitle(firstSymbol); + + WriteSymbol(firstSymbolTitle, out); + + return ToLower(text, textEnd - text, out) || firstSymbol != firstSymbolTitle; +} + +bool ToLower(wchar32* text, size_t length) noexcept { + const auto f = [](const wchar32 s) { return ToLower(s); }; + const auto* const textEnd = text + length; + if (ModifySequence(text, textEnd, f)) { + ModifySequence(text, textEnd, f); + return true; + } + return false; +} + +bool ToUpper(wchar32* text, size_t length) noexcept { + const auto f = [](const wchar32 s) { return ToUpper(s); }; + const auto* const textEnd = text + length; + if (ModifySequence(text, textEnd, f)) { + ModifySequence(text, textEnd, f); + return true; + } + return false; +} + +bool ToTitle(wchar32* text, size_t length) noexcept { + if (!length) { + return false; + } + + const auto* textEnd = text + length; + const auto firstSymbol = ReadSymbol(text, textEnd); + const auto firstSymbolTitle = ToTitle(firstSymbol); + + // avoid unnacessary writes to the memory + if (firstSymbol != firstSymbolTitle) { + WriteSymbol(firstSymbolTitle, text); + } else { + text = SkipSymbol(text, textEnd); + } + + return ToLower(text, textEnd - text) || firstSymbol != firstSymbolTitle; +} + +template +static TUtf16String ToSmthRet(const TWtringBuf text, size_t pos, size_t count, F&& f) { + pos = pos < text.size() ? pos : text.size(); + count = count < text.size() - pos ? count : text.size() - pos; + + auto res = TUtf16String::Uninitialized(text.size()); + auto* const resBegin = res.Detach(); + + if (pos) { + MemCopy(resBegin, text.data(), pos); + } + + f(text.data() + pos, count, resBegin + pos); + + if (count - pos != text.size()) { + MemCopy(resBegin + pos + count, text.data() + pos + count, text.size() - pos - count); + } + + return res; +} + +template +static TUtf32String ToSmthRet(const TUtf32StringBuf text, size_t pos, size_t count, F&& f) { + pos = pos < text.size() ? pos : text.size(); + count = count < text.size() - pos ? count : text.size() - pos; + + auto res = TUtf32String::Uninitialized(text.size()); + auto* const resBegin = res.Detach(); + + if (pos) { + MemCopy(resBegin, text.data(), pos); + } + + f(text.data() + pos, count, resBegin + pos); + + if (count - pos != text.size()) { + MemCopy(resBegin + pos + count, text.data() + pos + count, text.size() - pos - count); + } + + return res; +} + +TUtf16String ToLowerRet(const TWtringBuf text, size_t pos, size_t count) { + return ToSmthRet(text, pos, count, [](const wchar16* theText, size_t length, wchar16* out) { + ToLower(theText, length, out); + }); +} + +TUtf16String ToUpperRet(const TWtringBuf text, size_t pos, size_t count) { + return ToSmthRet(text, pos, count, [](const wchar16* theText, size_t length, wchar16* out) { + ToUpper(theText, length, out); + }); +} + +TUtf16String ToTitleRet(const TWtringBuf text, size_t pos, size_t count) { + return ToSmthRet(text, pos, count, [](const wchar16* theText, size_t length, wchar16* out) { + ToTitle(theText, length, out); + }); +} + +TUtf32String ToLowerRet(const TUtf32StringBuf text, size_t pos, size_t count) { + return ToSmthRet(text, pos, count, [](const wchar32* theText, size_t length, wchar32* out) { + ToLower(theText, length, out); + }); +} + +TUtf32String ToUpperRet(const TUtf32StringBuf text, size_t pos, size_t count) { + return ToSmthRet(text, pos, count, [](const wchar32* theText, size_t length, wchar32* out) { + ToUpper(theText, length, out); + }); +} + +TUtf32String ToTitleRet(const TUtf32StringBuf text, size_t pos, size_t count) { + return ToSmthRet(text, pos, count, [](const wchar32* theText, size_t length, wchar32* out) { + ToTitle(theText, length, out); + }); +} + +template +void EscapeHtmlChars(TUtf16String& str) { + static const TUtf16String lt(LT, Y_ARRAY_SIZE(LT)); + static const TUtf16String gt(GT, Y_ARRAY_SIZE(GT)); + static const TUtf16String amp(AMP, Y_ARRAY_SIZE(AMP)); + static const TUtf16String br(BR, Y_ARRAY_SIZE(BR)); + static const TUtf16String quot(QUOT, Y_ARRAY_SIZE(QUOT)); + + size_t escapedLen = 0; + + const TUtf16String& cs = str; + + for (size_t i = 0; i < cs.size(); ++i) + escapedLen += EscapedLen(cs[i]); + + if (escapedLen == cs.size()) + return; + + TUtf16String res; + res.reserve(escapedLen); + + size_t start = 0; + + for (size_t i = 0; i < cs.size(); ++i) { + const TUtf16String* ent = nullptr; + switch (cs[i]) { + case '<': + ent = < + break; + case '>': + ent = > + break; + case '&': + ent = & + break; + case '\"': + ent = " + break; + default: + if (insertBr && (cs[i] == '\r' || cs[i] == '\n')) { + ent = &br; + break; + } else + continue; + } + + res.append(cs.begin() + start, cs.begin() + i); + res.append(ent->begin(), ent->end()); + start = i + 1; + } + + res.append(cs.begin() + start, cs.end()); + res.swap(str); +} + +template void EscapeHtmlChars(TUtf16String& str); +template void EscapeHtmlChars(TUtf16String& str); diff --git a/util/charset/wide.h b/util/charset/wide.h new file mode 100644 index 00000000000..2b52bb4f12c --- /dev/null +++ b/util/charset/wide.h @@ -0,0 +1,880 @@ +#pragma once + +#include "recode_result.h" +#include "unidata.h" +#include "utf8.h" +#include "wide_specific.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef _sse2_ + #include +#endif + +template +class TTempArray; +using TCharTemp = TTempArray; + +namespace NDetail { + inline TString InStringMsg(const char* s, size_t len) { + return (len <= 50) ? " in string " + TString(s, len).Quote() : TString(); + } + + template + struct TSelector; + + template <> + struct TSelector { + template + static inline void WriteSymbol(wchar16 s, T& dest) noexcept { + dest.push_back(s); + } + }; + + template <> + struct TSelector { + template + static inline void WriteSymbol(wchar16 s, T& dest) noexcept { + *(dest++) = s; + } + }; + + inline wchar32 ReadSurrogatePair(const wchar16* chars) noexcept { + const wchar32 SURROGATE_OFFSET = static_cast(0x10000 - (0xD800 << 10) - 0xDC00); + wchar32 lead = chars[0]; + wchar32 tail = chars[1]; + + Y_ASSERT(IsW16SurrogateLead(lead)); + Y_ASSERT(IsW16SurrogateTail(tail)); + + return (static_cast(lead) << 10) + tail + SURROGATE_OFFSET; + } + + template + inline void WriteSurrogatePair(wchar32 s, T& dest) noexcept; + +} + +inline wchar16* SkipSymbol(wchar16* begin, const wchar16* end) noexcept { + return begin + W16SymbolSize(begin, end); +} +inline const wchar16* SkipSymbol(const wchar16* begin, const wchar16* end) noexcept { + return begin + W16SymbolSize(begin, end); +} +inline wchar32* SkipSymbol(wchar32* begin, const wchar32* end) noexcept { + Y_ASSERT(begin < end); + return begin + 1; +} +inline const wchar32* SkipSymbol(const wchar32* begin, const wchar32* end) noexcept { + Y_ASSERT(begin < end); + return begin + 1; +} + +inline wchar32 ReadSymbol(const wchar16* begin, const wchar16* end) noexcept { + Y_ASSERT(begin < end); + if (IsW16SurrogateLead(*begin)) { + if (begin + 1 < end && IsW16SurrogateTail(*(begin + 1))) + return ::NDetail::ReadSurrogatePair(begin); + + return BROKEN_RUNE; + } else if (IsW16SurrogateTail(*begin)) { + return BROKEN_RUNE; + } + + return *begin; +} + +inline wchar32 ReadSymbol(const wchar32* begin, const wchar32* end) noexcept { + Y_ASSERT(begin < end); + return *begin; +} + +//! presuming input data is either big enought of null terminated +inline wchar32 ReadSymbolAndAdvance(const char16_t*& begin) noexcept { + Y_ASSERT(*begin); + if (IsW16SurrogateLead(begin[0])) { + if (IsW16SurrogateTail(begin[1])) { + Y_ASSERT(begin[1] != 0); + const wchar32 c = ::NDetail::ReadSurrogatePair(begin); + begin += 2; + return c; + } + ++begin; + return BROKEN_RUNE; + } else if (IsW16SurrogateTail(begin[0])) { + ++begin; + return BROKEN_RUNE; + } + return *(begin++); +} + +//! presuming input data is either big enought of null terminated +inline wchar32 ReadSymbolAndAdvance(const char32_t*& begin) noexcept { + Y_ASSERT(*begin); + return *(begin++); +} + +inline wchar32 ReadSymbolAndAdvance(const wchar_t*& begin) noexcept { + // According to + // https://en.cppreference.com/w/cpp/language/types + // wchar_t holds UTF-16 on Windows and UTF-32 on Linux / macOS + // + // Apply reinterpret cast and dispatch to a proper type + +#ifdef _win_ + using TDistinctChar = char16_t; +#else + using TDistinctChar = char32_t; +#endif + const TDistinctChar*& distinctBegin = reinterpret_cast(begin); + wchar32 result = ReadSymbolAndAdvance(distinctBegin); + begin = reinterpret_cast(distinctBegin); + return result; +} + +inline wchar32 ReadSymbolAndAdvance(const char16_t*& begin, const char16_t* end) noexcept { + Y_ASSERT(begin < end); + if (IsW16SurrogateLead(begin[0])) { + if (begin + 1 != end && IsW16SurrogateTail(begin[1])) { + const wchar32 c = ::NDetail::ReadSurrogatePair(begin); + begin += 2; + return c; + } + ++begin; + return BROKEN_RUNE; + } else if (IsW16SurrogateTail(begin[0])) { + ++begin; + return BROKEN_RUNE; + } + return *(begin++); +} + +inline wchar32 ReadSymbolAndAdvance(const wchar32*& begin, const wchar32* end) noexcept { + Y_ASSERT(begin < end); + return *(begin++); +} + +inline wchar32 ReadSymbolAndAdvance(const wchar_t*& begin, const wchar_t* end) noexcept { + // According to + // https://en.cppreference.com/w/cpp/language/types + // wchar_t holds UTF-16 on Windows and UTF-32 on Linux / macOS + // + // Apply reinterpret cast and dispatch to a proper type + +#ifdef _win_ + using TDistinctChar = char16_t; +#else + using TDistinctChar = char32_t; +#endif + const TDistinctChar* distinctBegin = reinterpret_cast(begin); + const TDistinctChar* distinctEnd = reinterpret_cast(end); + wchar32 result = ::ReadSymbolAndAdvance(distinctBegin, distinctEnd); + begin = reinterpret_cast(distinctBegin); + return result; +} + +template +inline size_t WriteSymbol(wchar16 s, T& dest) noexcept { + ::NDetail::TSelector::value>::WriteSymbol(s, dest); + return 1; +} + +template +inline size_t WriteSymbol(wchar32 s, T& dest) noexcept { + if (s > 0xFFFF) { + if (s >= ::NUnicode::UnicodeInstancesLimit()) { + return WriteSymbol(static_cast(BROKEN_RUNE), dest); + } + + ::NDetail::WriteSurrogatePair(s, dest); + return 2; + } + + return WriteSymbol(static_cast(s), dest); +} + +inline bool WriteSymbol(wchar32 s, wchar16*& dest, const wchar16* destEnd) noexcept { + Y_ASSERT(dest < destEnd); + + if (s > 0xFFFF) { + if (s >= NUnicode::UnicodeInstancesLimit()) { + *(dest++) = static_cast(BROKEN_RUNE); + return true; + } + + if (dest + 2 > destEnd) + return false; + + ::NDetail::WriteSurrogatePair(s, dest); + } else { + *(dest++) = static_cast(s); + } + + return true; +} + +inline size_t WriteSymbol(wchar32 s, wchar32*& dest) noexcept { + *(dest++) = s; + return 1; +} + +inline bool WriteSymbol(wchar32 s, wchar32*& dest, const wchar32* destEnd) noexcept { + Y_ASSERT(dest < destEnd); + + *(dest++) = s; + + return true; +} + +template +inline void ::NDetail::WriteSurrogatePair(wchar32 s, T& dest) noexcept { + const wchar32 LEAD_OFFSET = 0xD800 - (0x10000 >> 10); + Y_ASSERT(s > 0xFFFF && s < ::NUnicode::UnicodeInstancesLimit()); + + wchar16 lead = LEAD_OFFSET + (static_cast(s >> 10)); + wchar16 tail = 0xDC00 + static_cast(s & 0x3FF); + Y_ASSERT(IsW16SurrogateLead(lead)); + Y_ASSERT(IsW16SurrogateTail(tail)); + + WriteSymbol(lead, dest); + WriteSymbol(tail, dest); +} + +class TCharIterator { +private: + const wchar16* Begin; + const wchar16* End; + +public: + inline explicit TCharIterator(const wchar16* end) + : Begin(end) + , End(end) + { + } + + inline TCharIterator(const wchar16* begin, const wchar16* end) + : Begin(begin) + , End(end) + { + } + + inline TCharIterator& operator++() { + Begin = SkipSymbol(Begin, End); + + return *this; + } + + inline bool operator==(const wchar16* other) const { + return Begin == other; + } + inline bool operator!=(const wchar16* other) const { + return !(*this == other); + } + + inline bool operator==(const TCharIterator& other) const { + return *this == other.Begin; + } + inline bool operator!=(const TCharIterator& other) const { + return *this != other.Begin; + } + + inline wchar32 operator*() const { + return ReadSymbol(Begin, End); + } + + inline const wchar16* Get() const { + return Begin; + } +}; + +namespace NDetail { + template + inline void UTF8ToWideImplScalar(const unsigned char*& cur, const unsigned char* last, TCharType*& dest) noexcept { + wchar32 rune = BROKEN_RUNE; + + while (cur != last) { + if (ReadUTF8CharAndAdvance(rune, cur, last) != RECODE_OK) { + if (robust) { + rune = BROKEN_RUNE; + ++cur; + } else { + break; + } + } + + Y_ASSERT(cur <= last); + WriteSymbol(rune, dest); + } + } + + template + inline void UTF16ToUTF32ImplScalar(const wchar16* cur, const wchar16* last, TCharType*& dest) noexcept { + wchar32 rune = BROKEN_RUNE; + + while (cur != last) { + rune = ReadSymbolAndAdvance(cur, last); + Y_ASSERT(cur <= last); + WriteSymbol(rune, dest); + } + } + + template + inline void UTF8ToWideImplSSE41(const unsigned char*& /*cur*/, const unsigned char* /*last*/, TCharType*& /*dest*/) noexcept { + } + + void UTF8ToWideImplSSE41(const unsigned char*& cur, const unsigned char* last, wchar16*& dest) noexcept; + + void UTF8ToWideImplSSE41(const unsigned char*& cur, const unsigned char* last, wchar32*& dest) noexcept; +} + +//! @return len if robust and position where encoding stopped if not +template +inline size_t UTF8ToWideImpl(const char* text, size_t len, TCharType* dest, size_t& written) noexcept { + const unsigned char* cur = reinterpret_cast(text); + const unsigned char* last = cur + len; + TCharType* p = dest; +#ifdef _sse_ //can't check for sse4, as we build most of arcadia without sse4 support even on platforms that support it + if (cur + 16 <= last && NX86::CachedHaveSSE41()) { + ::NDetail::UTF8ToWideImplSSE41(cur, last, p); + } +#endif + + ::NDetail::UTF8ToWideImplScalar(cur, last, p); + written = p - dest; + return cur - reinterpret_cast(text); +} + +template +inline size_t UTF8ToWideImpl(const char* text, size_t len, TCharType* dest, size_t& written) { + return UTF8ToWideImpl(text, len, dest, written); +} + +template +inline TUtf16String UTF8ToWide(const char* text, size_t len) { + TUtf16String w = TUtf16String::Uninitialized(len); + size_t written; + size_t pos = UTF8ToWideImpl(text, len, w.begin(), written); + if (pos != len) + ythrow yexception() << "failed to decode UTF-8 string at pos " << pos << ::NDetail::InStringMsg(text, len); + Y_ASSERT(w.size() >= written); + w.remove(written); + return w; +} + +template +inline bool UTF8ToWide(const char* text, size_t len, TCharType* dest, size_t& written) noexcept { + return UTF8ToWideImpl(text, len, dest, written) == len; +} + +//! converts text from UTF8 to unicode, stops immediately it UTF8 byte sequence is wrong +//! @attention destination buffer must be long enough to fit all characters of the text, +//! conversion stops if a broken symbol is met +//! @return @c true if all the text converted successfully, @c false - a broken symbol was found +template +inline bool UTF8ToWide(const char* text, size_t len, TCharType* dest, size_t& written) noexcept { + return UTF8ToWide(text, len, dest, written); +} + +template +inline TWtringBuf UTF8ToWide(const TStringBuf src, TUtf16String& dst) { + dst.ReserveAndResize(src.size()); + size_t written = 0; + UTF8ToWideImpl(src.data(), src.size(), dst.begin(), written); + dst.resize(written); + return dst; +} + +//! if not robust will stop at first error position +template +inline TUtf32StringBuf UTF8ToUTF32(const TStringBuf src, TUtf32String& dst) { + dst.ReserveAndResize(src.size()); + size_t written = 0; + UTF8ToWideImpl(src.data(), src.size(), dst.begin(), written); + dst.resize(written); + return dst; +} + +inline TWtringBuf UTF8ToWide(const TStringBuf src, TUtf16String& dst) { + return UTF8ToWide(src, dst); +} + +inline TUtf16String UTF8ToWide(const char* text, size_t len) { + return UTF8ToWide(text, len); +} + +template +inline TUtf16String UTF8ToWide(const TStringBuf s) { + return UTF8ToWide(s.data(), s.size()); +} + +template +inline TUtf32String UTF8ToUTF32(const TStringBuf s) { + TUtf32String r; + UTF8ToUTF32(s, r); + return r; +} + +inline TUtf16String UTF8ToWide(const TStringBuf s) { + return UTF8ToWide(s.data(), s.size()); +} + +//! converts text from unicode to UTF8 +//! @attention destination buffer must be long enough to fit all characters of the text, +//! @c WriteUTF8Char converts @c wchar32 into maximum 4 bytes of UTF8 so +//! destination buffer must have length equal to len * 4 +template +inline void WideToUTF8(const TCharType* text, size_t len, char* dest, size_t& written) { + const TCharType* const last = text + len; + unsigned char* p = reinterpret_cast(dest); + size_t runeLen; + for (const TCharType* cur = text; cur != last;) { + WriteUTF8Char(ReadSymbolAndAdvance(cur, last), runeLen, p); + Y_ASSERT(runeLen <= 4); + p += runeLen; + } + written = p - reinterpret_cast(dest); +} + +constexpr size_t WideToUTF8BufferSize(const size_t inputStringSize) noexcept { + return inputStringSize * 4; // * 4 because the conversion functions can convert unicode character into maximum 4 bytes of UTF8 +} + +inline TStringBuf WideToUTF8(const TWtringBuf src, TString& dst) { + dst.ReserveAndResize(WideToUTF8BufferSize(src.size())); + size_t written = 0; + WideToUTF8(src.data(), src.size(), dst.begin(), written); + Y_ASSERT(dst.size() >= written); + dst.remove(written); + return dst; +} + +inline TString WideToUTF8(const wchar16* text, size_t len) { + TString s = TString::Uninitialized(WideToUTF8BufferSize(len)); + size_t written = 0; + WideToUTF8(text, len, s.begin(), written); + Y_ASSERT(s.size() >= written); + s.remove(written); + return s; +} + +inline TString WideToUTF8(const wchar32* text, size_t len) { + TString s = TString::Uninitialized(WideToUTF8BufferSize(len)); + size_t written = 0; + WideToUTF8(text, len, s.begin(), written); + Y_ASSERT(s.size() >= written); + s.remove(written); + return s; +} + +inline TString WideToUTF8(const TWtringBuf w) { + return WideToUTF8(w.data(), w.size()); +} + +inline TString WideToUTF8(const TUtf32StringBuf w) { + return WideToUTF8(w.data(), w.size()); +} + +inline TUtf16String UTF32ToWide(const wchar32* begin, size_t len) { + TUtf16String res; + res.reserve(len); + + const wchar32* end = begin + len; + for (const wchar32* i = begin; i != end; ++i) { + WriteSymbol(*i, res); + } + + return res; +} + +// adopted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/string_util.cc +// Assuming that a pointer is the size of a "machine word", then +// uintptr_t is an integer type that is also a machine word. + +namespace NDetail { + using TMachineWord = uintptr_t; + const uintptr_t kMachineWordAlignmentMask = sizeof(TMachineWord) - 1; + + inline bool IsAlignedToMachineWord(const void* pointer) { + return !(reinterpret_cast(pointer) & kMachineWordAlignmentMask); + } + + template + inline T* AlignToMachineWord(T* pointer) { + return reinterpret_cast(reinterpret_cast(pointer) & ~kMachineWordAlignmentMask); + } + + template + struct NonASCIIMask; + + template <> + struct + NonASCIIMask<4, wchar16> { + static constexpr ui32 Value() { + return 0xFF80FF80U; + } + }; + + template <> + struct + NonASCIIMask<4, char> { + static constexpr ui32 Value() { + return 0x80808080U; + } + }; + + template <> + struct + NonASCIIMask<8, wchar16> { + static constexpr ui64 Value() { + return 0xFF80FF80FF80FF80ULL; + } + }; + + template <> + struct + NonASCIIMask<8, char> { + static constexpr ui64 Value() { + return 0x8080808080808080ULL; + } + }; + + template + inline bool DoIsStringASCIISlow(const TChar* first, const TChar* last) { + using TUnsignedChar = std::make_unsigned_t; + Y_ASSERT(first <= last); + for (; first != last; ++first) { + if (static_cast(*first) > 0x7F) { + return false; + } + } + return true; + } + + template + inline bool DoIsStringASCII(const TChar* first, const TChar* last) { + if (last - first < 10) { + return DoIsStringASCIISlow(first, last); + } + TMachineWord allCharBits = 0; + TMachineWord nonAsciiBitMask = NonASCIIMask::Value(); + + // Prologue: align the input. + while (!IsAlignedToMachineWord(first) && first != last) { + allCharBits |= *first; + ++first; + } + + // Compare the values of CPU word size. + const TChar* word_end = AlignToMachineWord(last); + const size_t loopIncrement = sizeof(TMachineWord) / sizeof(TChar); + while (first < word_end) { + allCharBits |= *(reinterpret_cast(first)); + first += loopIncrement; + + // fast exit + if (allCharBits & nonAsciiBitMask) { + return false; + } + } + + // Process the remaining bytes. + while (first != last) { + allCharBits |= *first; + ++first; + } + + return !(allCharBits & nonAsciiBitMask); + } + +#ifdef _sse2_ + inline bool DoIsStringASCIISSE(const unsigned char* first, const unsigned char* last) { + //scalar version for short strings + if (first + 8 > last) { + return ::NDetail::DoIsStringASCIISlow(first, last); + } + + alignas(16) unsigned char buf[16]; + + while (first + 16 <= last) { + memcpy(buf, first, 16); + __m128i chunk = _mm_load_si128(reinterpret_cast<__m128i*>(buf)); + + int asciiMask = _mm_movemask_epi8(chunk); + if (asciiMask) { + return false; + } + first += 16; + } + + if (first + 8 <= last) { + memcpy(buf, first, 8); + __m128i chunk = _mm_loadl_epi64(reinterpret_cast<__m128i*>(buf)); + + int asciiMask = _mm_movemask_epi8(chunk); + if (asciiMask) { + return false; + } + first += 8; + } + + return ::NDetail::DoIsStringASCIISlow(first, last); + } +#endif //_sse2_ + +} + +//! returns @c true if character sequence has no symbols with value greater than 0x7F +template +inline bool IsStringASCII(const TChar* first, const TChar* last) { + return ::NDetail::DoIsStringASCII(first, last); +} + +#ifdef _sse2_ +template <> +inline bool IsStringASCII(const unsigned char* first, const unsigned char* last) { + return ::NDetail::DoIsStringASCIISSE(first, last); +} +template <> +inline bool IsStringASCII(const char* first, const char* last) { + return ::NDetail::DoIsStringASCIISSE(reinterpret_cast(first), reinterpret_cast(last)); +} +#endif + +//! copies elements from one character sequence to another using memcpy +//! for compatibility only +template +inline void Copy(const TChar* first, size_t len, TChar* result) { + memcpy(result, first, len * sizeof(TChar)); +} + +template +inline void Copy(const TChar1* first, size_t len, TChar2* result) { + Copy(first, first + len, result); +} + +//! copies symbols from one character sequence to another without any conversion +//! @note this function can be used instead of the template constructor of @c std::basic_string: +//! template +//! basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator()); +//! and the family of template member functions: append, assign, insert, replace. +template +inline TStringType CopyTo(const TChar* first, const TChar* last) { + Y_ASSERT(first <= last); + TStringType str = TStringType::Uninitialized(last - first); + Copy(first, last, str.begin()); + return str; +} + +template +inline TStringType CopyTo(const TChar* s, size_t n) { + TStringType str = TStringType::Uninitialized(n); + Copy(s, n, str.begin()); + return str; +} + +inline TString WideToASCII(const TWtringBuf w) { + Y_ASSERT(IsStringASCII(w.begin(), w.end())); + return CopyTo(w.begin(), w.end()); +} + +inline TUtf16String ASCIIToWide(const TStringBuf s) { + Y_ASSERT(IsStringASCII(s.begin(), s.end())); + return CopyTo(s.begin(), s.end()); +} + +inline TUtf32String ASCIIToUTF32(const TStringBuf s) { + Y_ASSERT(IsStringASCII(s.begin(), s.end())); + return CopyTo(s.begin(), s.end()); +} + +//! returns @c true if string contains whitespace characters only +inline bool IsSpace(const wchar16* s, size_t n) { + if (n == 0) + return false; + + Y_ASSERT(s); + + const wchar16* const e = s + n; + for (const wchar16* p = s; p != e; ++p) { + if (!IsWhitespace(*p)) + return false; + } + return true; +} + +//! returns @c true if string contains whitespace characters only +inline bool IsSpace(const TWtringBuf s) { + return IsSpace(s.data(), s.length()); +} + +//! replaces multiple sequential whitespace characters with a single space character +void Collapse(TUtf16String& w); + +//! @return new length +size_t Collapse(wchar16* s, size_t n); + +//! Removes leading whitespace characters +TWtringBuf StripLeft(const TWtringBuf text) noexcept Y_WARN_UNUSED_RESULT; +void StripLeft(TUtf16String& text); + +//! Removes trailing whitespace characters +TWtringBuf StripRight(const TWtringBuf text) noexcept Y_WARN_UNUSED_RESULT; +void StripRight(TUtf16String& text); + +//! Removes leading and trailing whitespace characters +TWtringBuf Strip(const TWtringBuf text) noexcept Y_WARN_UNUSED_RESULT; +void Strip(TUtf16String& text); + +/* Check if given word is lowercase/uppercase. Will return false if string contains any + * non-alphabetical symbols. It is expected that `text` is a correct UTF-16 string. + * + * For example `IsLowerWord("hello")` will return `true`, when `IsLowerWord("hello there")` will + * return false because of the space in the middle of the string. Empty string is also considered + * lowercase. + */ +bool IsLowerWord(const TWtringBuf text) noexcept; +bool IsUpperWord(const TWtringBuf text) noexcept; + +/* Will check if given word starts with capital letter and the rest of the word is lowercase. Will + * return `false` for empty string. See also `IsLowerWord`. + */ +bool IsTitleWord(const TWtringBuf text) noexcept; + +/* Check if given string is lowercase/uppercase. Will return `true` if all alphabetic symbols are + * in proper case, all other symbols are ignored. It is expected that `text` is a correct UTF-16 + * string. + * + * For example `IsLowerWord("hello")` will return `true` and `IsLowerWord("hello there")` will + * also return true because. Empty string is also considered lowercase. + * + * NOTE: for any case where `IsLowerWord` returns `true` `IsLower` will also return `true`. + */ +bool IsLower(const TWtringBuf text) noexcept; +bool IsUpper(const TWtringBuf text) noexcept; + +/* Lowercase/uppercase given string inplace. Any alphabetic symbol will be converted to a proper + * case, the rest of the symbols will be kept the same. It is expected that `text` is a correct + * UTF-16 string. + * + * For example `ToLower("heLLo")` will return `"hello"`. + * + * @param text String to modify + * @param pos Position of the first character to modify + * @param count Length of the substring + * @returns `true` if `text` was changed + * + * NOTE: `pos` and `count` are measured in `wchar16`, not in codepoints. + */ +bool ToLower(TUtf16String& text, size_t pos = 0, size_t count = TUtf16String::npos); +bool ToUpper(TUtf16String& text, size_t pos = 0, size_t count = TUtf16String::npos); + +/* Lowercase/uppercase given string inplace. Any alphabetic symbol will be converted to a proper + * case, the rest of the symbols will be kept the same. It is expected that `text` is a correct + * UTF-32 string. + * + * For example `ToLower("heLLo")` will return `"hello"`. + * + * @param text String to modify + * @param pos Position of the first character to modify + * @param count Length of the substring + * @returns `true` if `text` was changed + * + * NOTE: `pos` and `count` are measured in `wchar16`, not in codepoints. + */ +bool ToLower(TUtf32String& /*text*/, size_t /*pos*/ = 0, size_t /*count*/ = TUtf16String::npos); +bool ToUpper(TUtf32String& /*text*/, size_t /*pos*/ = 0, size_t /*count*/ = TUtf16String::npos); + +/* Titlecase first symbol and lowercase the rest, see `ToLower` for more details. + */ +bool ToTitle(TUtf16String& text, size_t pos = 0, size_t count = TUtf16String::npos); + +/* Titlecase first symbol and lowercase the rest, see `ToLower` for more details. + */ +bool ToTitle(TUtf32String& /*text*/, size_t /*pos*/ = 0, size_t /*count*/ = TUtf16String::npos); + +/* @param text Pointer to the string to modify + * @param length Length of the string to modify + * @param out Pointer to the character array to write to + * + * NOTE: [text, text+length) and [out, out+length) should not interleave. + * + * TODO(yazevnul): replace these functions with `bool(const TWtringBuf, const TArrayRef)` + * overload. + */ +bool ToLower(const wchar16* text, size_t length, wchar16* out) noexcept; +bool ToUpper(const wchar16* text, size_t length, wchar16* out) noexcept; +bool ToTitle(const wchar16* text, size_t length, wchar16* out) noexcept; + +bool ToLower(const wchar32* text, size_t length, wchar32* out) noexcept; +bool ToUpper(const wchar32* text, size_t length, wchar32* out) noexcept; +bool ToTitle(const wchar32* text, size_t length, wchar32* out) noexcept; + +/* @param text Pointer to the string to modify + * @param length Length of the string to modify + * + * TODO(yazevnul): replace these functions with `bool(const TArrayRef)` overload. + */ +bool ToLower(wchar16* text, size_t length) noexcept; +bool ToUpper(wchar16* text, size_t length) noexcept; +bool ToTitle(wchar16* text, size_t length) noexcept; + +bool ToLower(wchar32* text, size_t length) noexcept; +bool ToUpper(wchar32* text, size_t length) noexcept; +bool ToTitle(wchar32* text, size_t length) noexcept; + +/* Convenience wrappers for `ToLower`, `ToUpper` and `ToTitle`. + */ +TUtf16String ToLowerRet(TUtf16String text, size_t pos = 0, size_t count = TUtf16String::npos) Y_WARN_UNUSED_RESULT; +TUtf16String ToUpperRet(TUtf16String text, size_t pos = 0, size_t count = TUtf16String::npos) Y_WARN_UNUSED_RESULT; +TUtf16String ToTitleRet(TUtf16String text, size_t pos = 0, size_t count = TUtf16String::npos) Y_WARN_UNUSED_RESULT; + +TUtf16String ToLowerRet(const TWtringBuf text, size_t pos = 0, size_t count = TWtringBuf::npos) Y_WARN_UNUSED_RESULT; +TUtf16String ToUpperRet(const TWtringBuf text, size_t pos = 0, size_t count = TWtringBuf::npos) Y_WARN_UNUSED_RESULT; +TUtf16String ToTitleRet(const TWtringBuf text, size_t pos = 0, size_t count = TWtringBuf::npos) Y_WARN_UNUSED_RESULT; + +TUtf32String ToLowerRet(const TUtf32StringBuf text, size_t pos = 0, size_t count = TWtringBuf::npos) Y_WARN_UNUSED_RESULT; +TUtf32String ToUpperRet(const TUtf32StringBuf text, size_t pos = 0, size_t count = TWtringBuf::npos) Y_WARN_UNUSED_RESULT; +TUtf32String ToTitleRet(const TUtf32StringBuf text, size_t pos = 0, size_t count = TWtringBuf::npos) Y_WARN_UNUSED_RESULT; + +//! replaces the '<', '>' and '&' characters in string with '<', '>' and '&' respectively +// insertBr=true - replace '\r' and '\n' with "
" +template +void EscapeHtmlChars(TUtf16String& str); + +//! returns number of characters in range. Handle surrogate pairs as one character. +inline size_t CountWideChars(const wchar16* b, const wchar16* e) { + size_t count = 0; + Y_ENSURE(b <= e, TStringBuf("invalid iterators")); + while (b < e) { + b = SkipSymbol(b, e); + ++count; + } + return count; +} + +inline size_t CountWideChars(const TWtringBuf str) { + return CountWideChars(str.begin(), str.end()); +} + +//! checks whether the range is valid UTF-16 sequence +inline bool IsValidUTF16(const wchar16* b, const wchar16* e) { + Y_ENSURE(b <= e, TStringBuf("invalid iterators")); + while (b < e) { + wchar32 symbol = ReadSymbolAndAdvance(b, e); + if (symbol == BROKEN_RUNE) + return false; + } + return true; +} + +inline bool IsValidUTF16(const TWtringBuf str) { + return IsValidUTF16(str.begin(), str.end()); +} diff --git a/util/charset/wide_specific.h b/util/charset/wide_specific.h new file mode 100644 index 00000000000..4ea765b94b6 --- /dev/null +++ b/util/charset/wide_specific.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +inline constexpr bool IsW16SurrogateLead(wchar16 c) noexcept { + return 0xD800 <= c && c <= 0xDBFF; +} + +inline constexpr bool IsW16SurrogateTail(wchar16 c) noexcept { + return 0xDC00 <= c && c <= 0xDFFF; +} + +inline size_t W16SymbolSize(const wchar16* begin, const wchar16* end) { + Y_ASSERT(begin < end); + + if ((begin + 1 != end) && IsW16SurrogateLead(*begin) && IsW16SurrogateTail(*(begin + 1))) { + return 2; + } + + return 1; +} diff --git a/util/charset/wide_sse41.cpp b/util/charset/wide_sse41.cpp new file mode 100644 index 00000000000..d1f2a74851e --- /dev/null +++ b/util/charset/wide_sse41.cpp @@ -0,0 +1,247 @@ +#include +#include + +#ifdef SSE41_STUB + +namespace NDetail { + void UTF8ToWideImplSSE41(const unsigned char*&, const unsigned char*, wchar16*&) noexcept { + } + void UTF8ToWideImplSSE41(const unsigned char*&, const unsigned char*, wchar32*&) noexcept { + } +} + +#else + + #include + + #include + #include + #include + +//processes to the first error, or until less then 16 bytes left +//most code taken from https://woboq.com/blog/utf-8-processing-using-simd.html + +//return dstAdvance 0 in case of problems +static Y_FORCE_INLINE ui32 Unpack16BytesIntoUtf16IfNoSurrogats(const unsigned char*& cur, __m128i& utf16Low, __m128i& utf16High) { + unsigned char curAligned[16]; + + memcpy(curAligned, cur, sizeof(__m128i)); + __m128i chunk = _mm_load_si128(reinterpret_cast(curAligned)); + + //only ascii characters - simple copy + if (!_mm_movemask_epi8(chunk)) { + utf16Low = _mm_unpacklo_epi8(chunk, _mm_setzero_si128()); + utf16High = _mm_unpackhi_epi8(chunk, _mm_setzero_si128()); + cur += 16; + return 16; + } + + __m128i chunkSigned = _mm_add_epi8(chunk, _mm_set1_epi8(0x80)); + __m128i isAsciiMask = _mm_cmpgt_epi8(chunk, _mm_set1_epi8(0)); + + __m128i cond2 = _mm_cmplt_epi8(_mm_set1_epi8(0xc2 - 1 - 0x80), chunkSigned); + __m128i state = _mm_set1_epi8(0x0 | (char)0x80); + + __m128i cond3 = _mm_cmplt_epi8(_mm_set1_epi8(0xe0 - 1 - 0x80), chunkSigned); + state = _mm_blendv_epi8(state, _mm_set1_epi8(0x2 | (char)0xc0), cond2); + + int sourceAdvance; + __m128i shifts; + __m128i chunkLow, chunkHigh; + + if (Y_LIKELY(!_mm_movemask_epi8(cond3))) { + //main case: no bloks of size 3 or 4 + + //rune len for start of multi-byte sequences (0 for b0... and b10..., 2 for b110..., etc.) + __m128i count = _mm_and_si128(state, _mm_set1_epi8(0x7)); + + __m128i countSub1 = _mm_subs_epu8(count, _mm_set1_epi8(0x1)); + + shifts = countSub1; + __m128i continuation1 = _mm_slli_si128(countSub1, 1); + + shifts = _mm_add_epi8(shifts, _mm_slli_si128(shifts, 1)); + shifts = _mm_add_epi8(shifts, _mm_slli_si128(shifts, 2)); + + __m128i counts = _mm_or_si128(count, continuation1); + + __m128i isBeginMultibyteMask = _mm_cmpgt_epi8(count, _mm_set1_epi8(0)); + __m128i needNoContinuationMask = _mm_cmpeq_epi8(continuation1, _mm_set1_epi8(0)); + __m128i isBeginMask = _mm_add_epi8(isBeginMultibyteMask, isAsciiMask); + //each symbol should be exactly one of ascii, continuation or begin + __m128i okMask = _mm_cmpeq_epi8(isBeginMask, needNoContinuationMask); + + if (_mm_movemask_epi8(okMask) != 0xFFFF) { + return 0; + } + + shifts = _mm_add_epi8(shifts, _mm_slli_si128(shifts, 4)); + + __m128i mask = _mm_and_si128(state, _mm_set1_epi8(0xf8)); + shifts = _mm_add_epi8(shifts, _mm_slli_si128(shifts, 8)); + + chunk = _mm_andnot_si128(mask, chunk); // from now on, we only have usefull bits + shifts = _mm_and_si128(shifts, _mm_cmplt_epi8(counts, _mm_set1_epi8(2))); // <=1 + + __m128i chunk_right = _mm_slli_si128(chunk, 1); + shifts = _mm_blendv_epi8(shifts, _mm_srli_si128(shifts, 1), + _mm_srli_si128(_mm_slli_epi16(shifts, 7), 1)); + + chunkLow = _mm_blendv_epi8(chunk, + _mm_or_si128(chunk, _mm_and_si128(_mm_slli_epi16(chunk_right, 6), _mm_set1_epi8(0xc0))), + _mm_cmpeq_epi8(counts, _mm_set1_epi8(1))); + + chunkHigh = _mm_and_si128(chunk, _mm_cmpeq_epi8(counts, _mm_set1_epi8(2))); + + shifts = _mm_blendv_epi8(shifts, _mm_srli_si128(shifts, 2), + _mm_srli_si128(_mm_slli_epi16(shifts, 6), 2)); + chunkHigh = _mm_srli_epi32(chunkHigh, 2); + + shifts = _mm_blendv_epi8(shifts, _mm_srli_si128(shifts, 4), + _mm_srli_si128(_mm_slli_epi16(shifts, 5), 4)); + + int c = _mm_extract_epi16(counts, 7); + sourceAdvance = !(c & 0x0200) ? 16 : 15; + + } else { + __m128i mask3 = _mm_slli_si128(cond3, 1); + + __m128i cond4 = _mm_cmplt_epi8(_mm_set1_epi8(0xf0 - 1 - 0x80), chunkSigned); + state = _mm_blendv_epi8(state, _mm_set1_epi8(0x3 | (char)0xe0), cond3); + + // 4 bytes sequences are not vectorize. Fall back to the scalar processing + if (Y_UNLIKELY(_mm_movemask_epi8(cond4))) { + return 0; + } + + //rune len for start of multi-byte sequences (0 for b0... and b10..., 2 for b110..., etc.) + __m128i count = _mm_and_si128(state, _mm_set1_epi8(0x7)); + + __m128i countSub1 = _mm_subs_epu8(count, _mm_set1_epi8(0x1)); + __m128i continuation2 = _mm_slli_si128(_mm_subs_epu8(count, _mm_set1_epi8(0x2)), 2); + + shifts = countSub1; + __m128i continuation1 = _mm_slli_si128(countSub1, 1); + + shifts = _mm_add_epi8(shifts, _mm_slli_si128(shifts, 1)); + __m128i continuationsRunelen = _mm_or_si128(continuation1, continuation2); + + shifts = _mm_add_epi8(shifts, _mm_slli_si128(shifts, 2)); + __m128i counts = _mm_or_si128(count, continuationsRunelen); + + __m128i isBeginMultibyteMask = _mm_cmpgt_epi8(count, _mm_set1_epi8(0)); + __m128i needNoContinuationMask = _mm_cmpeq_epi8(continuationsRunelen, _mm_set1_epi8(0)); + __m128i isBeginMask = _mm_add_epi8(isBeginMultibyteMask, isAsciiMask); + //each symbol should be exactly one of ascii, continuation or begin + __m128i okMask = _mm_cmpeq_epi8(isBeginMask, needNoContinuationMask); + + if (_mm_movemask_epi8(okMask) != 0xFFFF) { + return 0; + } + + shifts = _mm_add_epi8(shifts, _mm_slli_si128(shifts, 4)); + + __m128i mask = _mm_and_si128(state, _mm_set1_epi8(0xf8)); + shifts = _mm_add_epi8(shifts, _mm_slli_si128(shifts, 8)); + + chunk = _mm_andnot_si128(mask, chunk); // from now on, we only have usefull bits + shifts = _mm_and_si128(shifts, _mm_cmplt_epi8(counts, _mm_set1_epi8(2))); // <=1 + + __m128i chunk_right = _mm_slli_si128(chunk, 1); + shifts = _mm_blendv_epi8(shifts, _mm_srli_si128(shifts, 1), + _mm_srli_si128(_mm_slli_epi16(shifts, 7), 1)); + + chunkLow = _mm_blendv_epi8(chunk, + _mm_or_si128(chunk, _mm_and_si128(_mm_slli_epi16(chunk_right, 6), _mm_set1_epi8(0xc0))), + _mm_cmpeq_epi8(counts, _mm_set1_epi8(1))); + + chunkHigh = _mm_and_si128(chunk, _mm_cmpeq_epi8(counts, _mm_set1_epi8(2))); + + shifts = _mm_blendv_epi8(shifts, _mm_srli_si128(shifts, 2), + _mm_srli_si128(_mm_slli_epi16(shifts, 6), 2)); + chunkHigh = _mm_srli_epi32(chunkHigh, 2); + + shifts = _mm_blendv_epi8(shifts, _mm_srli_si128(shifts, 4), + _mm_srli_si128(_mm_slli_epi16(shifts, 5), 4)); + chunkHigh = _mm_or_si128(chunkHigh, + _mm_and_si128(_mm_and_si128(_mm_slli_epi32(chunk_right, 4), _mm_set1_epi8(0xf0)), + mask3)); + + int c = _mm_extract_epi16(counts, 7); + sourceAdvance = !(c & 0x0200) ? 16 : !(c & 0x02) ? 15 + : 14; + } + + shifts = _mm_blendv_epi8(shifts, _mm_srli_si128(shifts, 8), + _mm_srli_si128(_mm_slli_epi16(shifts, 4), 8)); + + chunkHigh = _mm_slli_si128(chunkHigh, 1); + + __m128i shuf = _mm_add_epi8(shifts, _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)); + + chunkLow = _mm_shuffle_epi8(chunkLow, shuf); + chunkHigh = _mm_shuffle_epi8(chunkHigh, shuf); + + utf16Low = _mm_unpacklo_epi8(chunkLow, chunkHigh); + utf16High = _mm_unpackhi_epi8(chunkLow, chunkHigh); + + ui32 s = _mm_extract_epi32(shifts, 3); + ui32 destAdvance = sourceAdvance - (0xff & (s >> (8 * (3 - 16 + sourceAdvance)))); + cur += sourceAdvance; + return destAdvance; +} + +namespace NDetail { + void UTF8ToWideImplSSE41(const unsigned char*& cur, const unsigned char* last, wchar16*& dest) noexcept { + alignas(16) wchar16 destAligned[16]; + + while (cur + 16 <= last) { + __m128i utf16Low; + __m128i utf16High; + ui32 dstAdvance = Unpack16BytesIntoUtf16IfNoSurrogats(cur, utf16Low, utf16High); + + if (dstAdvance == 0) { + break; + } + + _mm_store_si128(reinterpret_cast<__m128i*>(destAligned), utf16Low); + _mm_store_si128(reinterpret_cast<__m128i*>(destAligned) + 1, utf16High); + memcpy(dest, destAligned, sizeof(__m128i) * 2); + dest += dstAdvance; + } + //The rest will be handled sequencially. + // Possible improvement: go back to the vectorized processing after the error or the 4 byte sequence + } + + void UTF8ToWideImplSSE41(const unsigned char*& cur, const unsigned char* last, wchar32*& dest) noexcept { + alignas(16) wchar32 destAligned[16]; + + while (cur + 16 <= last) { + __m128i utf16Low; + __m128i utf16High; + ui32 dstAdvance = Unpack16BytesIntoUtf16IfNoSurrogats(cur, utf16Low, utf16High); + + if (dstAdvance == 0) { + break; + } + + //NOTE: we only work in case without surrogat pairs, so we can make simple copying with zeroes in 2 high bytes + __m128i utf32_lowlow = _mm_unpacklo_epi16(utf16Low, _mm_set1_epi8(0)); + __m128i utf32_lowhigh = _mm_unpackhi_epi16(utf16Low, _mm_set1_epi8(0)); + __m128i utf32_highlow = _mm_unpacklo_epi16(utf16High, _mm_set1_epi8(0)); + __m128i utf32_highhigh = _mm_unpackhi_epi16(utf16High, _mm_set1_epi8(0)); + + _mm_store_si128(reinterpret_cast<__m128i*>(destAligned), utf32_lowlow); + _mm_store_si128(reinterpret_cast<__m128i*>(destAligned) + 1, utf32_lowhigh); + _mm_store_si128(reinterpret_cast<__m128i*>(destAligned) + 2, utf32_highlow); + _mm_store_si128(reinterpret_cast<__m128i*>(destAligned) + 3, utf32_highhigh); + + memcpy(dest, destAligned, sizeof(__m128i) * 4); + dest += dstAdvance; + } + //The rest will be handled sequencially. + // Possible improvement: go back to the vectorized processing after the error or the 4 byte sequence + } +} + +#endif diff --git a/util/charset/wide_ut.cpp b/util/charset/wide_ut.cpp new file mode 100644 index 00000000000..b33dd0c0de9 --- /dev/null +++ b/util/charset/wide_ut.cpp @@ -0,0 +1,1799 @@ +#include "utf8.h" +#include "wide.h" + +#include + +#include + +#include + +namespace { + //! three UTF8 encoded russian letters (A, B, V) + const char utext[] = "\xd0\x90\xd0\x91\xd0\x92"; + + const char asciiLatinAlphabet[] = "ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz"; + const wchar16 wideLatinAlphabet[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'G', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'g', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0}; + const wchar16 wideCyrillicAlphabet[] = { + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x00}; + const char utf8CyrillicAlphabet[] = + "\xd0\x90\xd0\x91\xd0\x92\xd0\x93\xd0\x94\xd0\x95\xd0\x96\xd0\x97" + "\xd0\x98\xd0\x99\xd0\x9a\xd0\x9b\xd0\x9c\xd0\x9d\xd0\x9e\xd0\x9f" + "\xd0\xa0\xd0\xa1\xd0\xa2\xd0\xa3\xd0\xa4\xd0\xa5\xd0\xa6\xd0\xa7" + "\xd0\xa8\xd0\xa9\xd0\xaa\xd0\xab\xd0\xac\xd0\xad\xd0\xae\xd0\xaf" + "\xd0\xb0\xd0\xb1\xd0\xb2\xd0\xb3\xd0\xb4\xd0\xb5\xd0\xb6\xd0\xb7" + "\xd0\xb8\xd0\xb9\xd0\xba\xd0\xbb\xd0\xbc\xd0\xbd\xd0\xbe\xd0\xbf" + "\xd1\x80\xd1\x81\xd1\x82\xd1\x83\xd1\x84\xd1\x85\xd1\x86\xd1\x87" + "\xd1\x88\xd1\x89\xd1\x8a\xd1\x8b\xd1\x8c\xd1\x8d\xd1\x8e\xd1\x8f"; + + const wchar32 LEAD_BITS_MASK_2_BYTES = 0x1F; + const wchar32 LEAD_BITS_MASK_3_BYTES = 0x0F; + const wchar32 LEAD_BITS_MASK_4_BYTES = 0x07; + + wchar16 ws[] = { + 0x0009, + 0x000A, 0x2028, 0x2029, + 0x000B, + 0x000C, + 0x000D, + 0x0020, 0x1680, + 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x200B, + 0x202F, 0x205F, 0x3000, + 0x00A0}; + + const size_t CaseTestDataSize = 10; + wchar32 WideStringTestData[][CaseTestDataSize] = { + {0x01C4, 0x10428, 0x10429, 0x10447, 0x10441, 0x1C03, 0x00A0, 0x10400, 0x10415, 0x10437}, // original + {0x01C6, 0x10428, 0x10429, 0x10447, 0x10441, 0x1C03, 0x00A0, 0x10428, 0x1043D, 0x10437}, // lower + {0x01C4, 0x10400, 0x10401, 0x1041F, 0x10419, 0x1C03, 0x00A0, 0x10400, 0x10415, 0x1040F}, // upper + {0x01C5, 0x10428, 0x10429, 0x10447, 0x10441, 0x1C03, 0x00A0, 0x10428, 0x1043D, 0x10437}, // title + }; + + TUtf16String CreateUnicodeText() { + const int len = 256; + wchar16 text[len] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x00 - 0x0F + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x10 - 0x1F + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x20 - 0x2F + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x30 - 0x3F + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x40 - 0x4F + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x50 - 0x5F + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x60 - 0x6F + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x70 - 0x7F + + 0x0301, 0x00C4, 0x00D6, 0x00DC, 0x0104, 0x0106, 0x0118, 0x0141, 0x00E0, 0x00E2, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x0490, 0x00AD, // 0x80 - 0x8F + 0x00DF, 0x00E4, 0x00F6, 0x00FC, 0x0105, 0x0107, 0x0119, 0x0142, 0x00EB, 0x00EE, 0x00EF, 0x00F4, 0x00F9, 0x00FB, 0x0491, 0x92CF, // 0x90 - 0x9F + 0x00A0, 0x0143, 0x00D3, 0x015A, 0x017B, 0x0179, 0x046C, 0x00A7, 0x0401, 0x0462, 0x0472, 0x0474, 0x040E, 0x0406, 0x0404, 0x0407, // 0xA0 - 0xAF + 0x00B0, 0x0144, 0x00F3, 0x015B, 0x017C, 0x017A, 0x046D, 0x2116, 0x0451, 0x0463, 0x0473, 0x0475, 0x045E, 0x0456, 0x0454, 0x0457 // 0xB0 - 0xBF + }; + for (int i = 0; i < len; ++i) { + if (i <= 0x7F) { // ASCII characters without 0x7 and 0x1B + text[i] = static_cast(i); + } else if (i >= 0xC0 && i <= 0xFF) { // russian characters (without YO and yo) + text[i] = static_cast(i + 0x0350); // 0x0410 - 0x044F + } + } + return TUtf16String(text, len); + } + + TString CreateUTF8Text() { + char text[] = { + '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', + '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', + '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27', '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f', + '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f', + '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f', + '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f', + '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', + '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f', + '\xcc', '\x81', '\xc3', '\x84', '\xc3', '\x96', '\xc3', '\x9c', '\xc4', '\x84', '\xc4', '\x86', '\xc4', '\x98', '\xc5', '\x81', + '\xc3', '\xa0', '\xc3', '\xa2', '\xc3', '\xa7', '\xc3', '\xa8', '\xc3', '\xa9', '\xc3', '\xaa', '\xd2', '\x90', '\xc2', '\xad', + '\xc3', '\x9f', '\xc3', '\xa4', '\xc3', '\xb6', '\xc3', '\xbc', '\xc4', '\x85', '\xc4', '\x87', '\xc4', '\x99', '\xc5', '\x82', + '\xc3', '\xab', '\xc3', '\xae', '\xc3', '\xaf', '\xc3', '\xb4', '\xc3', '\xb9', '\xc3', '\xbb', '\xd2', '\x91', '\xe9', '\x8b', + '\x8f', '\xc2', '\xa0', '\xc5', '\x83', '\xc3', '\x93', '\xc5', '\x9a', '\xc5', '\xbb', '\xc5', '\xb9', '\xd1', '\xac', '\xc2', + '\xa7', '\xd0', '\x81', '\xd1', '\xa2', '\xd1', '\xb2', '\xd1', '\xb4', '\xd0', '\x8e', '\xd0', '\x86', '\xd0', '\x84', '\xd0', + '\x87', '\xc2', '\xb0', '\xc5', '\x84', '\xc3', '\xb3', '\xc5', '\x9b', '\xc5', '\xbc', '\xc5', '\xba', '\xd1', '\xad', '\xe2', + '\x84', '\x96', '\xd1', '\x91', '\xd1', '\xa3', '\xd1', '\xb3', '\xd1', '\xb5', '\xd1', '\x9e', '\xd1', '\x96', '\xd1', '\x94', + '\xd1', '\x97', '\xd0', '\x90', '\xd0', '\x91', '\xd0', '\x92', '\xd0', '\x93', '\xd0', '\x94', '\xd0', '\x95', '\xd0', '\x96', + '\xd0', '\x97', '\xd0', '\x98', '\xd0', '\x99', '\xd0', '\x9a', '\xd0', '\x9b', '\xd0', '\x9c', '\xd0', '\x9d', '\xd0', '\x9e', + '\xd0', '\x9f', '\xd0', '\xa0', '\xd0', '\xa1', '\xd0', '\xa2', '\xd0', '\xa3', '\xd0', '\xa4', '\xd0', '\xa5', '\xd0', '\xa6', + '\xd0', '\xa7', '\xd0', '\xa8', '\xd0', '\xa9', '\xd0', '\xaa', '\xd0', '\xab', '\xd0', '\xac', '\xd0', '\xad', '\xd0', '\xae', + '\xd0', '\xaf', '\xd0', '\xb0', '\xd0', '\xb1', '\xd0', '\xb2', '\xd0', '\xb3', '\xd0', '\xb4', '\xd0', '\xb5', '\xd0', '\xb6', + '\xd0', '\xb7', '\xd0', '\xb8', '\xd0', '\xb9', '\xd0', '\xba', '\xd0', '\xbb', '\xd0', '\xbc', '\xd0', '\xbd', '\xd0', '\xbe', + '\xd0', '\xbf', '\xd1', '\x80', '\xd1', '\x81', '\xd1', '\x82', '\xd1', '\x83', '\xd1', '\x84', '\xd1', '\x85', '\xd1', '\x86', + '\xd1', '\x87', '\xd1', '\x88', '\xd1', '\x89', '\xd1', '\x8a', '\xd1', '\x8b', '\xd1', '\x8c', '\xd1', '\x8d', '\xd1', '\x8e', + '\xd1', '\x8f'}; + return TString(text, Y_ARRAY_SIZE(text)); + } + + //! use this function to dump UTF8 text into a file in case of any changes + // void DumpUTF8Text() { + // TString s = WideToUTF8(UnicodeText); + // std::ofstream f("utf8.txt"); + // f << std::hex; + // for (int i = 0; i < (int)s.size(); ++i) { + // f << "0x" << std::setw(2) << std::setfill('0') << (int)(ui8)s[i] << ", "; + // if ((i + 1) % 16 == 0) + // f << std::endl; + // } + // } + + template + void CheckRecodeOK(wchar32 expected, unsigned char* first, size_t n) { + wchar32 w = 0; + const unsigned char* p = first; + + RECODE_RESULT r = ReadUTF8CharAndAdvance(w, p, first + n); + UNIT_ASSERT(w == expected); + UNIT_ASSERT(size_t(p - first) == n); + UNIT_ASSERT(r == RECODE_OK); + } + + template + void CheckBrokenSymbol(unsigned char* first, unsigned char* last) { + wchar32 w = 0; + const unsigned char* p = first; + + RECODE_RESULT r = ReadUTF8CharAndAdvance(w, p, last); + UNIT_ASSERT(w == BROKEN_RUNE); + UNIT_ASSERT(p - first == 0); + UNIT_ASSERT(r == RECODE_BROKENSYMBOL); + } + + void CheckEndOfInput(unsigned char* first, size_t n) { + wchar32 w = 0; + const unsigned char* p = first; + + RECODE_RESULT r = ReadUTF8CharAndAdvance(w, p, first + n); + (void)w; + UNIT_ASSERT(p - first == 0); + UNIT_ASSERT(r == RECODE_EOINPUT); + } + + void CheckCharLen(unsigned char* first, unsigned char* last, size_t len, RECODE_RESULT result) { + size_t n = 0; + RECODE_RESULT r = GetUTF8CharLen(n, first, last); + UNIT_ASSERT(n == len); + UNIT_ASSERT(r == result); + } +} + +class TConversionTest: public TTestBase { +private: + //! @note every of the text can have zeros in the middle + const TUtf16String UnicodeText_; + const TString Utf8Text_; + +private: + UNIT_TEST_SUITE(TConversionTest); + UNIT_TEST(TestReadUTF8Char); + UNIT_TEST(TestGetUTF8CharLen); + UNIT_TEST(TestWriteUTF8Char); + UNIT_TEST(TestUTF8ToWide); + UNIT_TEST(TestWideToUTF8); + UNIT_TEST(TestGetNumOfUTF8Chars); + UNIT_TEST(TestSubstrUTF8); + UNIT_TEST(TestUnicodeCase); + UNIT_TEST(TestUnicodeDetails); + UNIT_TEST(TestHexConversion); + UNIT_TEST_SUITE_END(); + +public: + TConversionTest() + : UnicodeText_(CreateUnicodeText()) + , Utf8Text_(CreateUTF8Text()) + { + } + + void TestReadUTF8Char(); + void TestGetUTF8CharLen(); + void TestWriteUTF8Char(); + void TestUTF8ToWide(); + void TestWideToUTF8(); + void TestGetNumOfUTF8Chars(); + void TestSubstrUTF8(); + void TestUnicodeCase(); + void TestUnicodeDetails(); + void TestHexConversion(); +}; + +UNIT_TEST_SUITE_REGISTRATION(TConversionTest); + +void TConversionTest::TestHexConversion() { + for (char ch = '0'; ch <= '9'; ++ch) { + UNIT_ASSERT(isxdigit(ch)); + UNIT_ASSERT(IsHexdigit(ch)); + } + + for (char ch = 'a'; ch <= 'f'; ++ch) { + UNIT_ASSERT(isxdigit(ch)); + UNIT_ASSERT(IsHexdigit(ch)); + } + + for (char ch = 'A'; ch <= 'F'; ++ch) { + UNIT_ASSERT(isxdigit(ch)); + UNIT_ASSERT(IsHexdigit(ch)); + } + + for (wchar16 i = std::numeric_limits::min(); i < std::numeric_limits::max(); ++i) { + if (IsHexdigit(i)) { + UNIT_ASSERT(isxdigit(char(i))); + } + } +} + +void TConversionTest::TestReadUTF8Char() { + wchar32 e; // expected unicode char + wchar32 c; + unsigned long u; // single UTF8 encoded character + unsigned char* const first = reinterpret_cast(&u); + unsigned char* const last = first + sizeof(u); + + // all ASCII characters are converted with no change (zero converted successfully as well) + for (c = 0; c <= 0x7F; ++c) { + u = c; + CheckRecodeOK(c, first, 1); + } + + // broken symbols from the second half of ASCII table (1000 0000 - 1011 1111) + for (c = 0x80; c <= 0xBF; ++c) { + u = c; + CheckBrokenSymbol(first, last); + } + + // overlong encoding: leading byte of 2-byte symbol: 1100 0000 - 1100 0001 + for (c = 0xC0; c <= 0xC1; ++c) { + u = c; + CheckBrokenSymbol(first, last); + + u |= 0x8000; + CheckBrokenSymbol(first, first + 2); + + CheckEndOfInput(first, 1); + } + + // leading byte of 2-byte symbol: 1100 0000 - 1101 1111 + for (c = 0xC2; c <= 0xDF; ++c) { + u = c; + CheckBrokenSymbol(first, last); + + u |= 0x8000; + // w: 0000 0000 0000 0000 - 0000 0111 1100 0000 + e = c & LEAD_BITS_MASK_2_BYTES; + e <<= 6; + CheckRecodeOK(e, first, 2); + + CheckEndOfInput(first, 1); + } + + // possible overlong encoding with leading byte 1110 0000 + { + u = c = 0xE0; + CheckBrokenSymbol(first, last); + + u |= 0x808000; + CheckBrokenSymbol(first, first + 3); + + u = c | 0x80A000; + e = 0x800; + CheckRecodeOK(e, first, 3); + + CheckEndOfInput(first, 2); + CheckEndOfInput(first, 1); + } + + // leading byte of 3-byte symbol: 1110 0001 - 1110 1111 + for (c = 0xE1; c <= 0xEF; ++c) { + u = c; + CheckBrokenSymbol(first, last); + + u |= 0x808000; + // w: 0000 0000 0000 0000 - 0000 0111 1100 0000 + e = c & LEAD_BITS_MASK_3_BYTES; + e <<= 12; + CheckRecodeOK(e, first, 3); + + CheckEndOfInput(first, 2); + CheckEndOfInput(first, 1); + } + + // leading byte of 3-byte symbol before surrogates: 1110 0001 - 1110 1100 + for (c = 0xE1; c <= 0xEC; ++c) { + u = c; + CheckBrokenSymbol(first, last); + + u |= 0x808000; + // w: 0000 0000 0000 0000 - 0000 0111 1100 0000 + e = c & LEAD_BITS_MASK_3_BYTES; + e <<= 12; + CheckRecodeOK(e, first, 3); + + CheckEndOfInput(first, 2); + CheckEndOfInput(first, 1); + } + + // rest of allowed characters before surrogate block + { + u = 0xED; + CheckBrokenSymbol(first, last); + + u |= 0xBF9F00; + e = 0xD7FF; + CheckRecodeOK(e, first, 3); + + CheckEndOfInput(first, 2); + CheckEndOfInput(first, 1); + } + + // rfc3629 section 4 forbids characters 0xD800 - 0xDFFF + { + u = 0xED; + CheckBrokenSymbol(first, last); + + u |= 0x80A000; + CheckBrokenSymbol(first, last); + + CheckEndOfInput(first, 2); + CheckEndOfInput(first, 1); + } + + // leading byte of 3-byte symbol after surrogates: 1110 1110 - 1110 1111 + for (c = 0xEE; c <= 0xEF; ++c) { + u = c; + CheckBrokenSymbol(first, last); + + u |= 0x808000; + // w: 0000 0000 0000 0000 - 0000 0111 1100 0000 + e = c & LEAD_BITS_MASK_3_BYTES; + e <<= 12; + CheckRecodeOK(e, first, 3); + + CheckEndOfInput(first, 2); + CheckEndOfInput(first, 1); + } + + // possible overlong encoding with leading byte 1111 0000 + { + u = c = 0xF0; + CheckBrokenSymbol(first, last); + + u |= 0x80808000; + CheckBrokenSymbol(first, first + 4); + + u = c | 0x80809000; + e = 0x10000; + CheckRecodeOK(e, first, 4); + + CheckEndOfInput(first, 3); + CheckEndOfInput(first, 2); + CheckEndOfInput(first, 1); + } + + // leading byte of 4-byte symbol: 1111 0001 - 1111 0111 + for (c = 0xF1; c <= 0xF3; ++c) { + u = c; + CheckBrokenSymbol(first, last); + + u |= 0x80808000; + // w: 0000 0000 0000 0000 - 0000 0111 1100 0000 + e = c & LEAD_BITS_MASK_4_BYTES; + e <<= 18; + CheckRecodeOK(e, first, 4); + + CheckEndOfInput(first, 3); + CheckEndOfInput(first, 2); + CheckEndOfInput(first, 1); + } + + // possible invalid code points with leading byte 1111 0100 + { + c = 0xF4; + + u = 0x80808000 | c; + e = c & LEAD_BITS_MASK_4_BYTES; + e <<= 18; + CheckRecodeOK(e, first, 4); + + // the largest possible Unicode code point + u = 0xBFBF8F00 | c; + e = 0x10FFFF; + CheckRecodeOK(e, first, 4); + + u = 0x80809000 | c; + CheckBrokenSymbol(first, last); + } + + // broken symbols: 1111 0101 - 1111 1111 + for (c = 0xF5; c <= 0xFF; ++c) { + u = c; + CheckBrokenSymbol(first, last); + } +} + +void TConversionTest::TestGetUTF8CharLen() { + wchar32 c; + unsigned long u; // single UTF8 encoded character + unsigned char* const first = reinterpret_cast(&u); + unsigned char* const last = first + sizeof(u); + + // all ASCII characters are converted with no change (zero converted successfully as well) + for (c = 0; c <= 0x7F; ++c) { + u = c; + CheckCharLen(first, last, 1, RECODE_OK); + } + + // broken symbols from the second half of ASCII table (1000 0000 - 1011 1111) + for (c = 0x80; c <= 0xBF; ++c) { + u = c; + CheckCharLen(first, last, 0, RECODE_BROKENSYMBOL); + } + + // leading byte of 2-byte symbol: 1100 0000 - 1101 1111 + for (c = 0xC0; c <= 0xDF; ++c) { + u = c; + CheckCharLen(first, last, 0, RECODE_BROKENSYMBOL); + + u |= 0x8000; + // w: 0000 0000 0000 0000 - 0000 0111 1100 0000 + CheckCharLen(first, last, 2, RECODE_OK); + + CheckCharLen(first, first + 1, 0, RECODE_EOINPUT); + } + + // leading byte of 3-byte symbol: 1110 0000 - 1110 1111 + for (c = 0xE0; c <= 0xEF; ++c) { + u = c; + CheckCharLen(first, last, 0, RECODE_BROKENSYMBOL); + + u |= 0x808000; + // w: 0000 0000 0000 0000 - 0000 0111 1100 0000 + CheckCharLen(first, last, 3, RECODE_OK); + + CheckCharLen(first, first + 2, 0, RECODE_EOINPUT); + CheckCharLen(first, first + 1, 0, RECODE_EOINPUT); + } + + // leading byte of 4-byte symbol: 1111 0000 - 1111 0111 + for (c = 0xF0; c <= 0xF3; ++c) { + u = c; + CheckCharLen(first, last, 0, RECODE_BROKENSYMBOL); + + u |= 0x80808000; + // w: 0000 0000 0000 0000 - 0000 0111 1100 0000 + CheckCharLen(first, last, 4, RECODE_OK); + + CheckCharLen(first, first + 3, 0, RECODE_EOINPUT); + CheckCharLen(first, first + 2, 0, RECODE_EOINPUT); + CheckCharLen(first, first + 1, 0, RECODE_EOINPUT); + } + + // broken symbols: 1111 1000 - 1111 1111 + for (c = 0xF8; c <= 0xFF; ++c) { + u = c; + CheckCharLen(first, last, 0, RECODE_BROKENSYMBOL); + } +} + +void TConversionTest::TestWriteUTF8Char() { + wchar32 w; + unsigned long u; // single UTF8 encoded character + size_t n; + + for (w = 0x00; w < 0x80; ++w) { + u = 0; + WriteUTF8Char(w, n, reinterpret_cast(&u)); + UNIT_ASSERT((u & 0xFFFFFF80) == 0x00000000); + UNIT_ASSERT(n == 1); + } + + for (w = 0x80; w < 0x800; ++w) { + u = 0; + WriteUTF8Char(w, n, reinterpret_cast(&u)); + UNIT_ASSERT((u & 0xFFFFC000) == 0x00008000); // see constants in ReadUTF8Char + UNIT_ASSERT(n == 2); + } + + for (w = 0x800; w < 0x10000; ++w) { + u = 0; + WriteUTF8Char(w, n, reinterpret_cast(&u)); + UNIT_ASSERT((u & 0xFFC0C000) == 0x00808000); // see constants in ReadUTF8Char + UNIT_ASSERT(n == 3); + } + + for (w = 0x10000; w < 0x80; ++w) { + WriteUTF8Char(w, n, reinterpret_cast(&u)); + UNIT_ASSERT((u & 0xC0C0C000) == 0x80808000); // see constants in ReadUTF8Char + UNIT_ASSERT(n == 4); + } +} + +static void TestSurrogates(const char* str, const wchar16* wide, size_t wideSize) { + TUtf16String w = UTF8ToWide(str); + + UNIT_ASSERT(w.size() == wideSize); + UNIT_ASSERT(!memcmp(w.c_str(), wide, wideSize)); + + TString s = WideToUTF8(w); + + UNIT_ASSERT(s == str); +} + +void TConversionTest::TestUTF8ToWide() { + TUtf16String w = UTF8ToWide(Utf8Text_); + + UNIT_ASSERT(w.size() == 256); + UNIT_ASSERT(w.size() == UnicodeText_.size()); + + for (int i = 0; i < 256; ++i) { + UNIT_ASSERT_VALUES_EQUAL(w[i], UnicodeText_[i]); + } + + wchar16 buffer[4] = {0}; + size_t written = 0; + // the function must extract 2 symbols only + bool result = UTF8ToWide(utext, 5, buffer, written); + UNIT_ASSERT(!result); + UNIT_ASSERT(buffer[0] == 0x0410); + UNIT_ASSERT(buffer[1] == 0x0411); + UNIT_ASSERT(buffer[2] == 0x0000); + UNIT_ASSERT(buffer[3] == 0x0000); + UNIT_ASSERT(written == 2); + + memset(buffer, 0, 4); + written = 0; + result = UTF8ToWide(utext, 1, buffer, written); + UNIT_ASSERT(!result); + UNIT_ASSERT(buffer[0] == 0x0000); + UNIT_ASSERT(buffer[1] == 0x0000); + UNIT_ASSERT(buffer[2] == 0x0000); + UNIT_ASSERT(buffer[3] == 0x0000); + UNIT_ASSERT(written == 0); + + w = UTF8ToWide(asciiLatinAlphabet, strlen(asciiLatinAlphabet)); + UNIT_ASSERT(w == wideLatinAlphabet); + w = UTF8ToWide(utf8CyrillicAlphabet, strlen(utf8CyrillicAlphabet)); + UNIT_ASSERT(w == wideCyrillicAlphabet); + + const char* utf8NonBMP = "\xf4\x80\x89\x84\xf4\x80\x89\x87\xf4\x80\x88\xba"; + wchar16 wNonBMPDummy[] = {0xDBC0, 0xDE44, 0xDBC0, 0xDE47, 0xDBC0, 0xDE3A}; + TestSurrogates(utf8NonBMP, wNonBMPDummy, Y_ARRAY_SIZE(wNonBMPDummy)); + + const char* utf8NonBMP2 = "ab\xf4\x80\x89\x87n"; + wchar16 wNonBMPDummy2[] = {'a', 'b', 0xDBC0, 0xDE47, 'n'}; + TestSurrogates(utf8NonBMP2, wNonBMPDummy2, Y_ARRAY_SIZE(wNonBMPDummy2)); + + UNIT_ASSERT_VALUES_EQUAL(WideToUTF8(UTF8ToWide(WideToUTF8(UTF8ToWide( + "m\xFB\xB2\xA5\xAA\xAFyeuse.sexwebcamz.com")))), + TString( + "m\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBDyeuse.sexwebcamz.com")); +} + +void TConversionTest::TestWideToUTF8() { + TString s = WideToUTF8(UnicodeText_); + size_t len = 0; + for (TUtf16String::const_iterator i = UnicodeText_.begin(), ie = UnicodeText_.end(); i != ie; ++i) { + len += UTF8RuneLenByUCS(*i); + } + + UNIT_ASSERT(s.size() == Utf8Text_.size()); + UNIT_ASSERT(s.size() == len); + + for (int i = 0; i < static_cast(s.size()); ++i) { + UNIT_ASSERT_VALUES_EQUAL(s[i], Utf8Text_[i]); + } +} + +void TConversionTest::TestGetNumOfUTF8Chars() { + size_t n = 0; + bool result = GetNumberOfUTF8Chars(Utf8Text_.c_str(), Utf8Text_.size(), n); + UNIT_ASSERT(result); + UNIT_ASSERT(n == 256); + + n = 0; + result = GetNumberOfUTF8Chars(utext, 5, n); + UNIT_ASSERT(!result); + UNIT_ASSERT(n == 2); + + n = 0; + result = GetNumberOfUTF8Chars(utext, 1, n); + UNIT_ASSERT(!result); + UNIT_ASSERT(n == 0); + + UNIT_ASSERT_EQUAL(GetNumberOfUTF8Chars("привет!"), 7); +} + +void TConversionTest::TestSubstrUTF8() { + TStringBuf utextBuf(utext, sizeof(utext)); + UNIT_ASSERT(SubstrUTF8(utextBuf, 0, 2) == utextBuf.substr(0, 4)); + UNIT_ASSERT(SubstrUTF8(utextBuf, 1, 1) == utextBuf.substr(2, 2)); + UNIT_ASSERT(SubstrUTF8(utextBuf, 1, 2) == utextBuf.substr(2, 4)); + UNIT_ASSERT(SubstrUTF8(utextBuf, 1, 3) == utextBuf.substr(2, 6)); +} + +inline bool MustBeSurrogate(wchar32 ch) { + return ch > 0xFFFF; +} + +void TConversionTest::TestUnicodeCase() { + // ToLower, ToUpper, ToTitle functions depend on equal size of both original and changed characters + for (wchar32 i = 0; i != NUnicode::UnicodeInstancesLimit(); ++i) { + UNIT_ASSERT(MustBeSurrogate(i) == MustBeSurrogate(ToLower(i))); + UNIT_ASSERT(MustBeSurrogate(i) == MustBeSurrogate(ToUpper(i))); + UNIT_ASSERT(MustBeSurrogate(i) == MustBeSurrogate(ToTitle(i))); + } +} + +void TConversionTest::TestUnicodeDetails() { + TUtf16String temp; + for (wchar32 i = 0; i != NUnicode::UnicodeInstancesLimit(); ++i) { + temp.clear(); + WriteSymbol(i, temp); + UNIT_ASSERT(temp.size() == W16SymbolSize(temp.c_str(), temp.c_str() + temp.size())); + } +} + +class TWideUtilTest: public TTestBase { + UNIT_TEST_SUITE(TWideUtilTest); + UNIT_TEST(TestCollapse); + UNIT_TEST(TestCollapseBuffer); + UNIT_TEST(TestStrip); + UNIT_TEST(TestIsSpace); + UNIT_TEST(TestEscapeHtmlChars); + UNIT_TEST(TestToLower); + UNIT_TEST(TestToUpper); + UNIT_TEST(TestWideString); + UNIT_TEST(TestCountWideChars); + UNIT_TEST(TestIsValidUTF16); + UNIT_TEST(TestIsStringASCII); + UNIT_TEST(TestIsLowerWordStr); + UNIT_TEST(TestIsUpperWordStr); + UNIT_TEST(TestIsTitleStr); + UNIT_TEST(TestIsLowerStr); + UNIT_TEST(TestIsUpperStr); + UNIT_TEST(TestToLowerStr); + UNIT_TEST(TestToUpperStr); + UNIT_TEST(TestToTitleStr); + UNIT_TEST_SUITE_END(); + +public: + void TestCollapse() { + TUtf16String s; + s.append(ws, Y_ARRAY_SIZE(ws)).append(3, 'a').append(ws, Y_ARRAY_SIZE(ws)).append(3, 'b').append(ws, Y_ARRAY_SIZE(ws)); + Collapse(s); + UNIT_ASSERT(s == ASCIIToWide(" aaa bbb ")); + { + const TUtf16String w(ASCIIToWide(" a b c ")); + s = w; + Collapse(s); + UNIT_ASSERT(s == w); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.c_str() == w.c_str()); // Collapse() does not change the string at all +#endif + } + s = ASCIIToWide(" 123 456 "); + Collapse(s); + UNIT_ASSERT(s == ASCIIToWide(" 123 456 ")); + + s = ASCIIToWide(" 1\n\n\n23\t 4\f\f56 "); + Collapse(s); + UNIT_ASSERT(s == ASCIIToWide(" 1 23 4 56 ")); + + s = ASCIIToWide(" 1\n\n\n\f\f56 "); + Collapse(s); + UNIT_ASSERT(s == ASCIIToWide(" 1 56 ")); + + s = ASCIIToWide(" 1\r\n,\n(\n23\t 4\f\f56 "); + Collapse(s); + UNIT_ASSERT(s == ASCIIToWide(" 1 , ( 23 4 56 ")); + + s = ASCIIToWide("1 23 "); + Collapse(s); + UNIT_ASSERT(s == ASCIIToWide("1 23 ")); + { + const TUtf16String w = ASCIIToWide(" "); + s = w; + Collapse(s); + UNIT_ASSERT(s == w); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.c_str() == w.c_str()); // Collapse() does not change the string at all +#endif + } + s = ASCIIToWide(" "); + Collapse(s); + UNIT_ASSERT(s == ASCIIToWide(" ")); + + s = ASCIIToWide(",\r\n\""); + Collapse(s); + UNIT_ASSERT(s == ASCIIToWide(", \"")); + + s = ASCIIToWide("-"); + Collapse(s); + UNIT_ASSERT(s == ASCIIToWide("-")); + + s.clear(); + Collapse(s); + UNIT_ASSERT(s == TUtf16String()); + } + + void TestCollapseBuffer() { + TUtf16String s; + s.append(ws, Y_ARRAY_SIZE(ws)).append(3, 'a').append(ws, Y_ARRAY_SIZE(ws)).append(3, 'b').append(ws, Y_ARRAY_SIZE(ws)); + size_t n = Collapse(s.begin(), s.size()); + s.resize(n); + UNIT_ASSERT(s == ASCIIToWide(" aaa bbb ")); + + s = ASCIIToWide(" a b c "); + n = Collapse(s.begin(), s.size()); + UNIT_ASSERT(n == s.size()); // length was not changed + UNIT_ASSERT(s == ASCIIToWide(" a b c ")); + + s = ASCIIToWide(" 123 456 "); + n = Collapse(s.begin(), s.size()); + s.resize(n); + UNIT_ASSERT(s == ASCIIToWide(" 123 456 ")); + + s = ASCIIToWide(" 1\n\n\n23\t 4\f\f56 "); + n = Collapse(s.begin(), s.size()); + s.resize(n); + UNIT_ASSERT(s == ASCIIToWide(" 1 23 4 56 ")); + + s = ASCIIToWide(" 1\n\n\n\f\f56 "); + n = Collapse(s.begin(), s.size()); + s.resize(n); + UNIT_ASSERT(s == ASCIIToWide(" 1 56 ")); + + s = ASCIIToWide(" 1\r\n,\n(\n23\t 4\f\f56 "); + n = Collapse(s.begin(), s.size()); + s.resize(n); + UNIT_ASSERT(s == ASCIIToWide(" 1 , ( 23 4 56 ")); + + s = ASCIIToWide("1 23 "); + n = Collapse(s.begin(), s.size()); + s.resize(n); + UNIT_ASSERT(s == ASCIIToWide("1 23 ")); + + s = ASCIIToWide(" "); + n = Collapse(s.begin(), s.size()); + UNIT_ASSERT(n == 1); + UNIT_ASSERT(s == ASCIIToWide(" ")); + + s = ASCIIToWide(" "); + n = Collapse(s.begin(), s.size()); + s.resize(n); + UNIT_ASSERT(s == ASCIIToWide(" ")); + + s = ASCIIToWide(",\r\n\""); + n = Collapse(s.begin(), s.size()); + s.resize(n); + UNIT_ASSERT(s == ASCIIToWide(", \"")); + + s = ASCIIToWide("-"); + n = Collapse(s.begin(), s.size()); + UNIT_ASSERT(n == 1); + UNIT_ASSERT(s == ASCIIToWide("-")); + + s = ASCIIToWide("\t"); + n = Collapse(s.begin(), s.size()); + UNIT_ASSERT(n == 1); + UNIT_ASSERT(s == ASCIIToWide(" ")); + + s.clear(); + n = Collapse(s.begin(), s.size()); + UNIT_ASSERT(n == 0); + UNIT_ASSERT(s == TUtf16String()); + } + + void TestStrip() { + TUtf16String s; + + Strip(s); + UNIT_ASSERT(s == TUtf16String()); + StripLeft(s); + UNIT_ASSERT(s == TUtf16String()); + StripRight(s); + UNIT_ASSERT(s == TUtf16String()); + + s = ASCIIToWide(" \t\r\n"); + Strip(s); + UNIT_ASSERT(s == TUtf16String()); + s = ASCIIToWide(" \t\r\n"); + StripLeft(s); + UNIT_ASSERT(s == TUtf16String()); + s = ASCIIToWide(" \t\r\n"); + StripRight(s); + UNIT_ASSERT(s == TUtf16String()); + + s = ASCIIToWide("\t\f\va \r\n"); + Strip(s); + UNIT_ASSERT(s == ASCIIToWide("a")); + s = ASCIIToWide("\t\f\va \r\n"); + StripLeft(s); + UNIT_ASSERT(s == ASCIIToWide("a \r\n")); + s = ASCIIToWide("\t\f\va \r\n"); + StripRight(s); + UNIT_ASSERT(s == ASCIIToWide("\t\f\va")); + + s = ASCIIToWide("\r\na\r\nb\t\tc\r\n"); + Strip(s); + UNIT_ASSERT(s == ASCIIToWide("a\r\nb\t\tc")); + s = ASCIIToWide("\r\na\r\nb\t\tc\r\n"); + StripLeft(s); + UNIT_ASSERT(s == ASCIIToWide("a\r\nb\t\tc\r\n")); + s = ASCIIToWide("\r\na\r\nb\t\tc\r\n"); + StripRight(s); + UNIT_ASSERT(s == ASCIIToWide("\r\na\r\nb\t\tc")); + + const TUtf16String w(ASCIIToWide("a b")); + s = w; + Strip(s); + UNIT_ASSERT(s == w); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.c_str() == w.c_str()); // Strip() does not change the string at all +#endif + s = w; + StripLeft(s); + UNIT_ASSERT(s == w); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.c_str() == w.c_str()); // Strip() does not change the string at all +#endif + s = w; + StripRight(s); + UNIT_ASSERT(s == w); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.c_str() == w.c_str()); // Strip() does not change the string at all +#endif + } + + void TestIsSpace() { + UNIT_ASSERT(!IsSpace(TUtf16String())); + + UNIT_ASSERT(IsSpace(ws, Y_ARRAY_SIZE(ws))); + + TUtf16String w; + w.assign(ws, Y_ARRAY_SIZE(ws)).append(TUtf16String(1, '!')); + UNIT_ASSERT(!IsSpace(w.c_str(), w.size())); + + w.assign(TUtf16String(1, '_')).append(ws, Y_ARRAY_SIZE(ws)); + UNIT_ASSERT(!IsSpace(w.c_str(), w.size())); + + w.assign(ws, Y_ARRAY_SIZE(ws)).append(TUtf16String(1, '$')).append(ws, Y_ARRAY_SIZE(ws)); + UNIT_ASSERT(!IsSpace(w.c_str(), w.size())); + } + + void TestEscapeHtmlChars() { + // characters from the first half of the ASCII table + for (wchar16 c = 1; c < 0x7F; ++c) { + TUtf16String w(1, c); + EscapeHtmlChars(w); + + switch (c) { + case '<': + UNIT_ASSERT(w == ASCIIToWide("<")); + break; + case '>': + UNIT_ASSERT(w == ASCIIToWide(">")); + break; + case '&': + UNIT_ASSERT(w == ASCIIToWide("&")); + break; + case '"': + UNIT_ASSERT(w == ASCIIToWide(""")); + break; + default: + UNIT_ASSERT(w == TUtf16String(1, c)); + break; + } + } + + for (wchar16 c = 1; c < 0x7F; ++c) { + TUtf16String w(1, c); + EscapeHtmlChars(w); + + switch (c) { + case '<': + UNIT_ASSERT(w == ASCIIToWide("<")); + break; + case '>': + UNIT_ASSERT(w == ASCIIToWide(">")); + break; + case '&': + UNIT_ASSERT(w == ASCIIToWide("&")); + break; + case '"': + UNIT_ASSERT(w == ASCIIToWide(""")); + break; + case '\r': + case '\n': + UNIT_ASSERT(w == ASCIIToWide("
")); + break; + default: + UNIT_ASSERT(w == TUtf16String(1, c)); + break; + } + } + } + + void TestToLower() { + const size_t n = 32; + wchar16 upperCase[n]; + std::copy(wideCyrillicAlphabet, wideCyrillicAlphabet + n, upperCase); + ToLower(upperCase, n); + UNIT_ASSERT(TWtringBuf(upperCase, n) == TWtringBuf(wideCyrillicAlphabet + n, n)); + } + + void TestToUpper() { + const size_t n = 32; + wchar16 lowerCase[n]; + std::copy(wideCyrillicAlphabet + n, wideCyrillicAlphabet + n * 2, lowerCase); + ToUpper(lowerCase, n); + UNIT_ASSERT(TWtringBuf(lowerCase, n) == TWtringBuf(wideCyrillicAlphabet, n)); + } + + void TestWideString() { + const TUtf16String original = UTF32ToWide(WideStringTestData[0], CaseTestDataSize); + const TUtf16String lower = UTF32ToWide(WideStringTestData[1], CaseTestDataSize); + const TUtf16String upper = UTF32ToWide(WideStringTestData[2], CaseTestDataSize); + const TUtf16String title = UTF32ToWide(WideStringTestData[3], CaseTestDataSize); + TUtf16String temp; + + temp = original; + temp.to_lower(); + UNIT_ASSERT(temp == lower); + + temp = original; + ToLower(temp.begin(), temp.size()); + UNIT_ASSERT(temp == lower); + + temp = original; + temp.to_upper(); + UNIT_ASSERT(temp == upper); + + temp = original; + ToUpper(temp.begin(), temp.size()); + UNIT_ASSERT(temp == upper); + + temp = original; + temp.to_title(); + UNIT_ASSERT(temp == title); + + temp = original; + ToTitle(temp.begin(), temp.size()); + UNIT_ASSERT(temp == title); + + TVector buffer(WideStringTestData[0], WideStringTestData[0] + CaseTestDataSize); + std::reverse(buffer.begin(), buffer.end()); + const TUtf16String reversed = UTF32ToWide(buffer.data(), buffer.size()); + + temp = original; + ReverseInPlace(temp); + UNIT_ASSERT(temp == reversed); + } + + void TestCountWideChars() { + UNIT_ASSERT_EQUAL(CountWideChars(UTF8ToWide("привет!")), 7); + TUtf16String wideStr = UTF8ToWide("\xf0\x9f\x92\xb8привет!"); + UNIT_ASSERT_EQUAL(wideStr.size(), 9); + UNIT_ASSERT_EQUAL(CountWideChars(wideStr), 8); + } + + void TestIsValidUTF16() { + static wchar16 str1[] = {'h', 'e', 'l', 'l', 'o', '!', 0}; + static wchar16 str2[] = {'h', 'e', 'l', 'l', 'o', 0xD842, 0xDEAD, '!', 0}; + static wchar16 str3[] = {'h', 'e', 'l', 'l', 'o', 0xD842, '!', 0}; + static wchar16 str4[] = {'h', 'e', 'l', 'l', 'o', 0xDEAD, 0xD842, '!', 0}; + static wchar16 str5[] = {'h', 'e', 'l', 'l', 'o', 0xD842, 0xDEAD, 0xDEAD, '!', 0}; + UNIT_ASSERT(IsValidUTF16(TWtringBuf(str1))); + UNIT_ASSERT(IsValidUTF16(TWtringBuf(str2))); + UNIT_ASSERT(!IsValidUTF16(TWtringBuf(str3))); + UNIT_ASSERT(!IsValidUTF16(TWtringBuf(str4))); + UNIT_ASSERT(!IsValidUTF16(TWtringBuf(str5))); + } + + void TestIsStringASCII() { + static char charAscii[] = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; + static wchar16 char16Ascii[] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', + 'B', 'C', 'D', 'E', 'F', '0', '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F', 0}; + + // Test a variety of the fragment start positions and lengths in order to make + // sure that bit masking in IsStringASCII works correctly. + // Also, test that a non-ASCII character will be detected regardless of its + // position inside the string. + { + const size_t stringLength = Y_ARRAY_SIZE(charAscii) - 1; + for (size_t offset = 0; offset < 8; ++offset) { + for (size_t len = 0, maxLen = stringLength - offset; len < maxLen; ++len) { + UNIT_ASSERT(IsStringASCII(charAscii + offset, charAscii + offset + len)); + for (size_t charPos = offset; charPos < len; ++charPos) { + charAscii[charPos] |= '\x80'; + UNIT_ASSERT(!IsStringASCII(charAscii + offset, charAscii + offset + len)); + charAscii[charPos] &= ~'\x80'; + } + } + } + } + + { + const size_t stringLength = Y_ARRAY_SIZE(char16Ascii) - 1; + for (size_t offset = 0; offset < 4; ++offset) { + for (size_t len = 0, maxLen = stringLength - offset; len < maxLen; ++len) { + UNIT_ASSERT(IsStringASCII(char16Ascii + offset, char16Ascii + offset + len)); + + for (size_t charPos = offset; charPos < len; ++charPos) { + char16Ascii[charPos] |= 0x80; + UNIT_ASSERT( + !IsStringASCII(char16Ascii + offset, char16Ascii + offset + len)); + + char16Ascii[charPos] &= ~0x80; + // Also test when the upper half is non-zero. + char16Ascii[charPos] |= 0x100; + UNIT_ASSERT( + !IsStringASCII(char16Ascii + offset, char16Ascii + offset + len)); + char16Ascii[charPos] &= ~0x100; + } + } + } + } + } + + void TestIsLowerWordStr() { + UNIT_ASSERT(IsLowerWord(TWtringBuf())); + UNIT_ASSERT(IsLowerWord(UTF8ToWide(""))); + UNIT_ASSERT(IsLowerWord(UTF8ToWide("test"))); + UNIT_ASSERT(IsLowerWord(UTF8ToWide("тест"))); // "тест" is "test" in russian (cyrrilic) + UNIT_ASSERT(!IsLowerWord(UTF8ToWide("тест тест"))); + UNIT_ASSERT(!IsLowerWord(UTF8ToWide("тест100500"))); + + UNIT_ASSERT(!IsLowerWord(UTF8ToWide("Test"))); + UNIT_ASSERT(!IsLowerWord(UTF8ToWide("tesT"))); + UNIT_ASSERT(!IsLowerWord(UTF8ToWide("tEst"))); + + UNIT_ASSERT(!IsLowerWord(UTF8ToWide("Тест"))); + UNIT_ASSERT(!IsLowerWord(UTF8ToWide("теСт"))); + UNIT_ASSERT(!IsLowerWord(UTF8ToWide("тесТ"))); + } + + void TestIsUpperWordStr() { + UNIT_ASSERT(IsUpperWord(TWtringBuf())); + UNIT_ASSERT(IsUpperWord(UTF8ToWide(""))); + UNIT_ASSERT(IsUpperWord(UTF8ToWide("TEST"))); + UNIT_ASSERT(IsUpperWord(UTF8ToWide("ТЕСТ"))); + UNIT_ASSERT(!IsUpperWord(UTF8ToWide("тест тест"))); + UNIT_ASSERT(!IsUpperWord(UTF8ToWide("тест100500"))); + + UNIT_ASSERT(!IsUpperWord(UTF8ToWide("Test"))); + UNIT_ASSERT(!IsUpperWord(UTF8ToWide("tesT"))); + UNIT_ASSERT(!IsUpperWord(UTF8ToWide("tEst"))); + + UNIT_ASSERT(!IsUpperWord(UTF8ToWide("Тест"))); + UNIT_ASSERT(!IsUpperWord(UTF8ToWide("теСт"))); + UNIT_ASSERT(!IsUpperWord(UTF8ToWide("тесТ"))); + } + + void TestIsTitleStr() { + UNIT_ASSERT(!IsTitleWord(TWtringBuf())); + UNIT_ASSERT(!IsTitleWord(UTF8ToWide(""))); + UNIT_ASSERT(!IsTitleWord(UTF8ToWide("t"))); + UNIT_ASSERT(!IsTitleWord(UTF8ToWide("й"))); + UNIT_ASSERT(IsTitleWord(UTF8ToWide("T"))); + UNIT_ASSERT(IsTitleWord(UTF8ToWide("Й"))); + UNIT_ASSERT(IsTitleWord(UTF8ToWide("Test"))); + UNIT_ASSERT(IsTitleWord(UTF8ToWide("Тест"))); + UNIT_ASSERT(!IsTitleWord(UTF8ToWide("тест тест"))); + UNIT_ASSERT(!IsTitleWord(UTF8ToWide("тест100500"))); + UNIT_ASSERT(!IsTitleWord(UTF8ToWide("Тест тест"))); + UNIT_ASSERT(!IsTitleWord(UTF8ToWide("Тест100500"))); + + UNIT_ASSERT(!IsTitleWord(UTF8ToWide("tesT"))); + UNIT_ASSERT(!IsTitleWord(UTF8ToWide("tEst"))); + + UNIT_ASSERT(!IsTitleWord(UTF8ToWide("теСт"))); + UNIT_ASSERT(!IsTitleWord(UTF8ToWide("тесТ"))); + } + + void TestIsLowerStr() { + UNIT_ASSERT(IsLower(TWtringBuf())); + UNIT_ASSERT(IsLower(UTF8ToWide(""))); + UNIT_ASSERT(IsLower(UTF8ToWide("test"))); + UNIT_ASSERT(IsLower(UTF8ToWide("тест"))); // "тест" is "test" in russian (cyrrilic) + UNIT_ASSERT(IsLower(UTF8ToWide("тест тест"))); + UNIT_ASSERT(IsLower(UTF8ToWide("тест100500"))); + + UNIT_ASSERT(!IsLower(UTF8ToWide("Test"))); + UNIT_ASSERT(!IsLower(UTF8ToWide("tesT"))); + UNIT_ASSERT(!IsLower(UTF8ToWide("tEst"))); + + UNIT_ASSERT(!IsLower(UTF8ToWide("Тест"))); + UNIT_ASSERT(!IsLower(UTF8ToWide("теСт"))); + UNIT_ASSERT(!IsLower(UTF8ToWide("тесТ"))); + } + + void TestIsUpperStr() { + UNIT_ASSERT(IsUpper(TWtringBuf())); + UNIT_ASSERT(IsUpper(UTF8ToWide(""))); + UNIT_ASSERT(IsUpper(UTF8ToWide("TEST"))); + UNIT_ASSERT(IsUpper(UTF8ToWide("ТЕСТ"))); + UNIT_ASSERT(IsUpper(UTF8ToWide("ТЕСТ ТЕСТ"))); + UNIT_ASSERT(IsUpper(UTF8ToWide("ТЕСТ100500"))); + + UNIT_ASSERT(!IsUpper(UTF8ToWide("Test"))); + UNIT_ASSERT(!IsUpper(UTF8ToWide("tesT"))); + UNIT_ASSERT(!IsUpper(UTF8ToWide("tEst"))); + + UNIT_ASSERT(!IsUpper(UTF8ToWide("Тест"))); + UNIT_ASSERT(!IsUpper(UTF8ToWide("теСт"))); + UNIT_ASSERT(!IsUpper(UTF8ToWide("тесТ"))); + } + + void TestToLowerStr() { + // In these test and test for `ToUpper` and `ToTitle` we are checking that string keep + // pointing to the same piece of memory we are doing it the following way: + // + // TUtf16String s = ... + // const auto copy = s; + // ... + // UNIT_ASSERT(s.data() == copy.data()) + // + // It saves us a couple lines (we are reusing `copy` later) and if one day `TString` will + // become non-refcounted we'll need to rewrite it to something like: + // + // TUtf16String s = ... + // const auto* const data = s.data(); + // const auto length = s.length(); + // ... + // UNIT_ASSERT(s.data() == data); + // UNIT_ASSERT(s.length() == length); + { + TUtf16String s; + auto writableCopy = s; + const auto copy = s; + const TUtf16String lower; + + UNIT_ASSERT(!ToLower(s)); + UNIT_ASSERT(s == lower); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(!ToLower(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == lower); + + UNIT_ASSERT(!ToLower(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == lower); + + UNIT_ASSERT(ToLowerRet(copy) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy)) == lower); + } + { + TUtf16String s = UTF8ToWide(""); + auto writableCopy = s; + const auto copy = s; + const TUtf16String lower; + + UNIT_ASSERT(!ToLower(s)); + UNIT_ASSERT(s == lower); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(!ToLower(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == lower); + + UNIT_ASSERT(!ToLower(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == lower); + + UNIT_ASSERT(ToLowerRet(copy) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy)) == lower); + } + { + TUtf16String s; + const auto copy = s; + const TUtf16String lower; + + UNIT_ASSERT(!ToLower(s, 100500)); + UNIT_ASSERT(s == lower); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(ToLowerRet(copy, 100500) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy), 100500) == lower); + } + { + TUtf16String s; + const auto copy = s; + const TUtf16String lower; + + UNIT_ASSERT(!ToLower(s, 100500, 1111)); + UNIT_ASSERT(s == lower); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(ToLowerRet(copy, 100500, 1111) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy), 100500, 1111) == lower); + } + { + auto s = UTF8ToWide("Й"); + auto writableCopy = s; + const auto copy = s; + const auto lower = UTF8ToWide("й"); + + UNIT_ASSERT(ToLower(s)); + UNIT_ASSERT(s == lower); + + UNIT_ASSERT(ToLower(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == lower); + + UNIT_ASSERT(ToLower(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == lower); + + UNIT_ASSERT(ToLowerRet(copy) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy)) == lower); + } + { + auto s = UTF8ToWide("й"); + auto writableCopy = s; + const auto copy = s; + const auto lower = UTF8ToWide("й"); + + UNIT_ASSERT(!ToLower(s)); + UNIT_ASSERT(s == lower); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(!ToLower(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == lower); + + UNIT_ASSERT(!ToLower(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == lower); + + UNIT_ASSERT(ToLowerRet(copy) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy)) == lower); + } + { + auto s = UTF8ToWide("тест"); + auto writableCopy = s; + const auto copy = s; + const auto lower = UTF8ToWide("тест"); + + UNIT_ASSERT(!ToLower(s)); + UNIT_ASSERT(s == lower); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(!ToLower(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == lower); + + UNIT_ASSERT(!ToLower(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == lower); + + UNIT_ASSERT(ToLowerRet(copy) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy)) == lower); + } + { + auto s = UTF8ToWide("Тест"); + auto writableCopy = s; + const auto copy = s; + const auto lower = UTF8ToWide("тест"); + + UNIT_ASSERT(ToLower(s)); + UNIT_ASSERT(s == lower); + + UNIT_ASSERT(ToLower(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == lower); + + UNIT_ASSERT(ToLower(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == lower); + + UNIT_ASSERT(ToLowerRet(copy) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy)) == lower); + } + { + TUtf16String s = UTF8ToWide("тЕст"); + const auto copy = s; + const auto lower = UTF8ToWide("тест"); + + UNIT_ASSERT(ToLower(s)); + UNIT_ASSERT(s == UTF8ToWide("тест")); + + UNIT_ASSERT(ToLowerRet(copy) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy)) == lower); + } + { + auto s = UTF8ToWide("тЕст"); + const auto copy = s; + const auto lower = UTF8ToWide("тЕст"); + + UNIT_ASSERT(!ToLower(s, 2)); + UNIT_ASSERT(s == lower); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(ToLowerRet(copy, 2) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy), 2) == lower); + } + { + auto s = UTF8ToWide("теСт"); + const auto copy = s; + const auto lower = UTF8ToWide("тест"); + + UNIT_ASSERT(ToLower(s, 2)); + UNIT_ASSERT(s == lower); + + UNIT_ASSERT(ToLowerRet(copy, 2) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy), 2) == lower); + } + { + auto s = UTF8ToWide("теСт"); + const auto copy = s; + const auto lower = UTF8ToWide("теСт"); + + UNIT_ASSERT(!ToLower(s, 3, 1)); + UNIT_ASSERT(s == copy); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(ToLowerRet(copy, 3, 1) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy), 3, 1) == lower); + } + { + auto s = UTF8ToWide("теСт"); + const auto copy = s; + const auto lower = UTF8ToWide("теСт"); + + UNIT_ASSERT(!ToLower(s, 3, 100500)); + UNIT_ASSERT(s == copy); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(ToLowerRet(copy, 3, 100500) == lower); + UNIT_ASSERT(ToLowerRet(TWtringBuf(copy), 3, 100500) == lower); + } + } + + void TestToUpperStr() { + { + TUtf16String s; + auto writableCopy = s; + const auto copy = s; + const TUtf16String upper; + + UNIT_ASSERT(!ToUpper(s)); + UNIT_ASSERT(s == upper); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(!ToUpper(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(!ToUpper(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(ToUpperRet(copy) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy)) == upper); + } + { + auto s = UTF8ToWide(""); + auto writableCopy = s; + const auto copy = s; + const TUtf16String upper; + + UNIT_ASSERT(!ToUpper(s)); + UNIT_ASSERT(s == upper); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(!ToUpper(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(!ToUpper(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(ToUpperRet(copy) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy)) == upper); + } + { + TUtf16String s; + auto writableCopy = s; + const auto copy = s; + const TUtf16String upper; + + UNIT_ASSERT(!ToUpper(s, 100500)); + UNIT_ASSERT(s == upper); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(!ToUpper(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(!ToUpper(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(ToUpperRet(copy, 100500) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy), 100500) == upper); + } + { + TUtf16String s; + const auto copy = s; + const TUtf16String upper; + + UNIT_ASSERT(!ToUpper(s, 100500, 1111)); + UNIT_ASSERT(s == upper); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(ToUpperRet(copy, 100500, 1111) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy), 100500, 1111) == upper); + } + { + auto s = UTF8ToWide("й"); + auto writableCopy = s; + const auto copy = s; + const auto upper = UTF8ToWide("Й"); + + UNIT_ASSERT(ToUpper(s)); + UNIT_ASSERT(s == upper); + + UNIT_ASSERT(ToUpper(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(ToUpper(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(ToUpperRet(copy) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy)) == upper); + } + { + auto s = UTF8ToWide("Й"); + auto writableCopy = s; + const auto copy = s; + const auto upper = UTF8ToWide("Й"); + + UNIT_ASSERT(!ToUpper(s)); + UNIT_ASSERT(s == copy); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(!ToUpper(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(!ToUpper(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(ToUpperRet(copy) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy)) == upper); + } + { + auto s = UTF8ToWide("тест"); + auto writableCopy = s; + const auto copy = s; + const auto upper = UTF8ToWide("ТЕСТ"); + + UNIT_ASSERT(ToUpper(s)); + UNIT_ASSERT(s == upper); + + UNIT_ASSERT(ToUpper(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(ToUpper(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(ToUpperRet(copy) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy)) == upper); + } + { + auto s = UTF8ToWide("Тест"); + auto writableCopy = s; + const auto copy = s; + const auto upper = UTF8ToWide("ТЕСТ"); + + UNIT_ASSERT(ToUpper(s)); + UNIT_ASSERT(s == upper); + + UNIT_ASSERT(ToUpper(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(ToUpper(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(ToUpperRet(copy) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy)) == upper); + } + { + auto s = UTF8ToWide("тЕст"); + auto writableCopy = s; + const auto copy = s; + const auto upper = UTF8ToWide("ТЕСТ"); + + UNIT_ASSERT(ToUpper(s)); + UNIT_ASSERT(s == upper); + + UNIT_ASSERT(ToUpper(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(ToUpper(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == upper); + + UNIT_ASSERT(ToUpperRet(copy) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy)) == upper); + } + { + auto s = UTF8ToWide("тЕст"); + const auto copy = s; + const auto upper = UTF8ToWide("тЕСТ"); + + UNIT_ASSERT(ToUpper(s, 2)); + UNIT_ASSERT(s == upper); + + UNIT_ASSERT(ToUpperRet(copy, 2) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy), 2) == upper); + } + { + auto s = UTF8ToWide("теСт"); + const auto copy = s; + const auto upper = UTF8ToWide("теСТ"); + + UNIT_ASSERT(ToUpper(s, 2)); + UNIT_ASSERT(s == upper); + + UNIT_ASSERT(ToUpperRet(copy, 2) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy), 2) == upper); + } + { + auto s = UTF8ToWide("теСт"); + const auto copy = s; + const auto upper = UTF8ToWide("теСТ"); + + UNIT_ASSERT(ToUpper(s, 3, 1)); + UNIT_ASSERT(s == upper); + + UNIT_ASSERT(ToUpperRet(copy, 3, 1) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy), 3, 1) == upper); + } + { + auto s = UTF8ToWide("теСт"); + const auto copy = s; + const auto upper = UTF8ToWide("теСТ"); + + UNIT_ASSERT(ToUpper(s, 3, 100500)); + UNIT_ASSERT(s == upper); + + UNIT_ASSERT(ToUpperRet(copy, 3, 100500) == upper); + UNIT_ASSERT(ToUpperRet(TWtringBuf(copy), 3, 100500) == upper); + } + } + + void TestToTitleStr() { + { + TUtf16String s; + auto writableCopy = s; + const auto copy = s; + const TUtf16String title; + + UNIT_ASSERT(!ToTitle(s)); + UNIT_ASSERT(s == title); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(!ToTitle(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(!ToTitle(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(ToTitleRet(copy) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy)) == title); + } + { + auto s = UTF8ToWide(""); + auto writableCopy = s; + const auto copy = s; + const TUtf16String title; + + UNIT_ASSERT(!ToTitle(s)); + UNIT_ASSERT(s == title); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(!ToTitle(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(!ToTitle(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(ToTitleRet(copy) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy)) == title); + } + { + TUtf16String s; + const auto copy = s; + const TUtf16String title; + + UNIT_ASSERT(!ToTitle(s, 100500)); + UNIT_ASSERT(s == title); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(ToTitleRet(copy) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy)) == title); + } + { + TUtf16String s; + const auto copy = s; + const TUtf16String title; + + UNIT_ASSERT(!ToTitle(s, 100500, 1111)); + UNIT_ASSERT(s == title); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(ToTitleRet(copy) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy)) == title); + } + { + auto s = UTF8ToWide("й"); + auto writableCopy = s; + const auto copy = s; + const auto title = UTF8ToWide("Й"); + + UNIT_ASSERT(ToTitle(s)); + UNIT_ASSERT(s == title); + + UNIT_ASSERT(ToTitle(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(ToTitle(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(ToTitleRet(copy) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy)) == title); + } + { + auto s = UTF8ToWide("Й"); + auto writableCopy = s; + const auto copy = s; + const auto title = UTF8ToWide("Й"); + + UNIT_ASSERT(!ToTitle(s)); + UNIT_ASSERT(s == title); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(!ToTitle(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(!ToTitle(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(ToTitleRet(copy) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy)) == title); + } + { + auto s = UTF8ToWide("тест"); + auto writableCopy = s; + const auto copy = s; + const auto title = UTF8ToWide("Тест"); + + UNIT_ASSERT(ToTitle(s)); + UNIT_ASSERT(s == title); + + UNIT_ASSERT(ToTitle(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(ToTitle(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(ToTitleRet(copy) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy)) == title); + } + { + auto s = UTF8ToWide("Тест"); + auto writableCopy = s; + const auto copy = s; + const auto title = UTF8ToWide("Тест"); + + UNIT_ASSERT(!ToTitle(s)); + UNIT_ASSERT(s == title); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(!ToTitle(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(!ToTitle(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(ToTitleRet(copy) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy)) == title); + } + { + auto s = UTF8ToWide("тЕст"); + auto writableCopy = s; + const auto copy = s; + const auto title = UTF8ToWide("Тест"); + + UNIT_ASSERT(ToTitle(s)); + UNIT_ASSERT(s == title); + + UNIT_ASSERT(ToTitle(writableCopy.Detach(), writableCopy.size())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(ToTitle(copy.data(), copy.size(), writableCopy.Detach())); + UNIT_ASSERT(writableCopy == title); + + UNIT_ASSERT(ToTitleRet(copy) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy)) == title); + } + { + auto s = UTF8ToWide("тЕст"); + const auto copy = s; + const auto title = UTF8ToWide("тЕСт"); + + UNIT_ASSERT(ToTitle(s, 2)); + UNIT_ASSERT(s == title); + + UNIT_ASSERT(ToTitleRet(copy, 2) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy), 2) == title); + } + { + auto s = UTF8ToWide("теСт"); + const auto copy = s; + const auto title = UTF8ToWide("теСт"); + + UNIT_ASSERT(!ToTitle(s, 2)); + UNIT_ASSERT(s == title); +#ifndef TSTRING_IS_STD_STRING + UNIT_ASSERT(s.data() == copy.data()); +#endif + + UNIT_ASSERT(ToTitleRet(copy, 2) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy), 2) == title); + } + { + auto s = UTF8ToWide("теСт"); + const auto copy = s; + const auto title = UTF8ToWide("теСТ"); + + UNIT_ASSERT(ToTitle(s, 3, 1)); + UNIT_ASSERT(s == title); + + UNIT_ASSERT(ToTitleRet(copy, 3, 1) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy), 3, 1) == title); + } + { + auto s = UTF8ToWide("теСт"); + const auto copy = s; + const auto title = UTF8ToWide("теСТ"); + + UNIT_ASSERT(ToTitle(s, 3, 100500)); + UNIT_ASSERT(s == title); + + UNIT_ASSERT(ToTitleRet(copy, 3, 100500) == title); + UNIT_ASSERT(ToTitleRet(TWtringBuf(copy), 3, 100500) == title); + } + } +}; + +UNIT_TEST_SUITE_REGISTRATION(TWideUtilTest); diff --git a/util/charset/ya.make b/util/charset/ya.make new file mode 100644 index 00000000000..e1a6f9be9c7 --- /dev/null +++ b/util/charset/ya.make @@ -0,0 +1,32 @@ +LIBRARY() + +NO_UTIL() + +IF (TSTRING_IS_STD_STRING) + CFLAGS(GLOBAL -DTSTRING_IS_STD_STRING) +ENDIF() + +JOIN_SRCS( + all_charset.cpp + generated/unidata.cpp + recode_result.cpp + unicode_table.cpp + unidata.cpp + utf8.cpp + wide.cpp +) + +IF (ARCH_X86_64 AND NOT DISABLE_INSTRUCTION_SETS) + SRC_C_SSE41(wide_sse41.cpp) +ELSE() + SRC( + wide_sse41.cpp + -DSSE41_STUB + ) +ENDIF() + +END() + +RECURSE_FOR_TESTS( + ut +) diff --git a/util/datetime/base.cpp b/util/datetime/base.cpp new file mode 100644 index 00000000000..83a17660fdf --- /dev/null +++ b/util/datetime/base.cpp @@ -0,0 +1,335 @@ +#include "base.h" + +#include +#include +#include +#include +#include +#include +#include + +TString Strftime(const char* format, const struct tm* tm) { + size_t size = Max(strlen(format) * 2 + 1, 107); + for (;;) { + TTempBuf buf(size); + int r = strftime(buf.Data(), buf.Size(), format, tm); + if (r != 0) { + return TString(buf.Data(), r); + } + size *= 2; + } +} + +template <> +TDuration FromStringImpl(const char* s, size_t len) { + return TDuration::Parse(TStringBuf(s, len)); +} + +template <> +bool TryFromStringImpl(const char* s, size_t len, TDuration& result) { + return TDuration::TryParse(TStringBuf(s, len), result); +} + +namespace { + template + struct TPad { + int I; + }; + + template + inline TPad Pad(int i) { + return {i}; + } + + inline IOutputStream& operator<<(IOutputStream& o, const TPad<2>& p) { + if (p.I < 10) { + if (p.I >= 0) { + o << '0'; + } + } + + return o << p.I; + } + + inline IOutputStream& operator<<(IOutputStream& o, const TPad<4>& p) { + if (p.I < 1000) { + if (p.I >= 0) { + if (p.I < 10) { + o << '0' << '0' << '0'; + } else if (p.I < 100) { + o << '0' << '0'; + } else { + o << '0'; + } + } + } + + return o << p.I; + } + + inline IOutputStream& operator<<(IOutputStream& o, const TPad<6>& p) { + if (p.I < 100000) { + if (p.I >= 0) { + if (p.I < 10) { + o << '0' << '0' << '0' << '0' << '0'; + } else if (p.I < 100) { + o << '0' << '0' << '0' << '0'; + } else if (p.I < 1000) { + o << '0' << '0' << '0'; + } else if (p.I < 10000) { + o << '0' << '0'; + } else { + o << '0'; + } + } + } + + return o << p.I; + } + + void WriteMicroSecondsToStream(IOutputStream& os, ui32 microSeconds) { + os << '.' << Pad<6>(microSeconds); + } + + void WriteTmToStream(IOutputStream& os, const struct tm& theTm) { + os << Pad<4>(theTm.tm_year + 1900) << '-' << Pad<2>(theTm.tm_mon + 1) << '-' << Pad<2>(theTm.tm_mday) << 'T' + << Pad<2>(theTm.tm_hour) << ':' << Pad<2>(theTm.tm_min) << ':' << Pad<2>(theTm.tm_sec); + } + + template + void WritePrintableLocalTimeToStream(IOutputStream& os, const ::NPrivate::TPrintableLocalTime& timeToPrint) { + const TInstant& momentToPrint = timeToPrint.MomentToPrint; + struct tm localTime; + momentToPrint.LocalTime(&localTime); + WriteTmToStream(os, localTime); + if (!PrintUpToSeconds) { + WriteMicroSecondsToStream(os, momentToPrint.MicroSecondsOfSecond()); + } +#ifndef _win_ + i64 utcOffsetInMinutes = localTime.tm_gmtoff / 60; +#else + TIME_ZONE_INFORMATION tz; + if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_INVALID) { + ythrow TSystemError() << "Failed to get the system time zone"; + } + i64 utcOffsetInMinutes = -tz.Bias; +#endif + if (utcOffsetInMinutes == 0) { + os << 'Z'; + } else { + if (utcOffsetInMinutes < 0) { + os << '-'; + utcOffsetInMinutes = -utcOffsetInMinutes; + } else { + os << '+'; + } + os << Pad<2>(utcOffsetInMinutes / 60); + if (iso) { + os << ':'; + } + os << Pad<2>(utcOffsetInMinutes % 60); + } + } +} + +template <> +void Out(IOutputStream& os, TTypeTraits::TFuncParam duration) { + os << duration.Seconds(); + WriteMicroSecondsToStream(os, duration.MicroSecondsOfSecond()); + os << 's'; +} + +template <> +void Out(IOutputStream& os, TTypeTraits::TFuncParam instant) { + char buf[64]; + auto len = FormatDate8601(buf, sizeof(buf), instant.TimeT()); + + // shouldn't happen due to current implementation of FormatDate8601() and GmTimeR() + Y_ENSURE(len, TStringBuf("Out: year does not fit into an integer")); + + os.Write(buf, len - 1 /* 'Z' */); + WriteMicroSecondsToStream(os, instant.MicroSecondsOfSecond()); + os << 'Z'; +} + +template <> +void Out<::NPrivate::TPrintableLocalTime>(IOutputStream& os, TTypeTraits<::NPrivate::TPrintableLocalTime>::TFuncParam localTime) { + WritePrintableLocalTimeToStream(os, localTime); +} + +template <> +void Out<::NPrivate::TPrintableLocalTime>(IOutputStream& os, TTypeTraits<::NPrivate::TPrintableLocalTime>::TFuncParam localTime) { + WritePrintableLocalTimeToStream(os, localTime); +} + +template <> +void Out<::NPrivate::TPrintableLocalTime>(IOutputStream& os, TTypeTraits<::NPrivate::TPrintableLocalTime>::TFuncParam localTime) { + WritePrintableLocalTimeToStream(os, localTime); +} + +template <> +void Out<::NPrivate::TPrintableLocalTime>(IOutputStream& os, TTypeTraits<::NPrivate::TPrintableLocalTime>::TFuncParam localTime) { + WritePrintableLocalTimeToStream(os, localTime); +} + +TString TDuration::ToString() const { + return ::ToString(*this); +} + +TString TInstant::ToString() const { + return ::ToString(*this); +} + +TString TInstant::ToRfc822String() const { + return FormatGmTime("%a, %d %b %Y %H:%M:%S GMT"); +} + +TString TInstant::ToStringUpToSeconds() const { + char buf[64]; + auto len = FormatDate8601(buf, sizeof(buf), TimeT()); + if (!len) { + ythrow yexception() << "TInstant::ToStringUpToSeconds: year does not fit into an integer"; + } + return TString(buf, len); +} + +TString TInstant::ToIsoStringLocal() const { + return ::ToString(FormatIsoLocal(*this)); +} + +TString TInstant::ToStringLocal() const { + return ::ToString(FormatLocal(*this)); +} + +TString TInstant::ToRfc822StringLocal() const { + return FormatLocalTime("%a, %d %b %Y %H:%M:%S %Z"); +} + +TString TInstant::ToIsoStringLocalUpToSeconds() const { + return ::ToString(FormatIsoLocalUpToSeconds(*this)); +} + +TString TInstant::ToStringLocalUpToSeconds() const { + return ::ToString(FormatLocalUpToSeconds(*this)); +} + +TString TInstant::FormatLocalTime(const char* format) const noexcept { + struct tm theTm; + LocalTime(&theTm); + return Strftime(format, &theTm); +} + +TString TInstant::FormatGmTime(const char* format) const noexcept { + struct tm theTm; + GmTime(&theTm); + return Strftime(format, &theTm); +} + +::NPrivate::TPrintableLocalTime FormatIsoLocal(TInstant instant) { + return ::NPrivate::TPrintableLocalTime(instant); +} + +::NPrivate::TPrintableLocalTime FormatLocal(TInstant instant) { + return ::NPrivate::TPrintableLocalTime(instant); +} + +::NPrivate::TPrintableLocalTime FormatIsoLocalUpToSeconds(TInstant instant) { + return ::NPrivate::TPrintableLocalTime(instant); +} + +::NPrivate::TPrintableLocalTime FormatLocalUpToSeconds(TInstant instant) { + return ::NPrivate::TPrintableLocalTime(instant); +} + +void Sleep(TDuration duration) { + NanoSleep(duration.NanoSeconds()); +} + +void sprint_gm_date(char* buf, time_t when, long* sec) { + struct tm theTm; + ::Zero(theTm); + GmTimeR(&when, &theTm); + DateToString(buf, theTm); + if (sec) { + *sec = seconds(theTm); + } +} + +void DateToString(char* buf, const struct tm& theTm) { + Y_ENSURE(0 <= theTm.tm_year + 1900 && theTm.tm_year + 1900 <= 9999, "invalid year " + ToString(theTm.tm_year + 1900) + ", year should be in range [0, 9999]"); + + snprintf(buf, DATE_BUF_LEN, "%04d%02d%02d", theTm.tm_year + 1900, theTm.tm_mon + 1, theTm.tm_mday); +} + +void DateToString(char* buf, time_t when, long* sec) { + struct tm theTm; + localtime_r(&when, &theTm); + + DateToString(buf, theTm); + + if (sec) { + *sec = seconds(theTm); + } +} + +TString DateToString(const struct tm& theTm) { + char buf[DATE_BUF_LEN]; + DateToString(buf, theTm); + return buf; +} + +TString DateToString(time_t when, long* sec) { + char buf[DATE_BUF_LEN]; + DateToString(buf, when, sec); + return buf; +} + +TString YearToString(const struct tm& theTm) { + Y_ENSURE(0 <= theTm.tm_year + 1900 && theTm.tm_year + 1900 <= 9999, "invalid year " + ToString(theTm.tm_year + 1900) + ", year should be in range [0, 9999]"); + char buf[16]; + snprintf(buf, 16, "%04d", theTm.tm_year + 1900); + return buf; +} + +TString YearToString(time_t when) { + struct tm theTm; + localtime_r(&when, &theTm); + + return YearToString(theTm); +} + +bool sscan_date(const char* date, struct tm& theTm) { + int year, mon, mday; + if (sscanf(date, "%4d%2d%2d", &year, &mon, &mday) != 3) { + return false; + } + theTm.tm_year = year - 1900; + theTm.tm_mon = mon - 1; + theTm.tm_mday = mday; + return true; +} + +size_t FormatDate8601(char* buf, size_t len, time_t when) { + struct tm theTm; + struct tm* ret = GmTimeR(&when, &theTm); + + if (ret) { + TMemoryOutput out(buf, len); + + WriteTmToStream(out, theTm); + out << 'Z'; + + return out.Buf() - buf; + } + + return 0; +} + +void SleepUntil(TInstant instant) { + TInstant now = TInstant::Now(); + if (instant <= now) { + return; + } + TDuration duration = instant - now; + Sleep(duration); +} diff --git a/util/datetime/base.h b/util/datetime/base.h new file mode 100644 index 00000000000..a0fc71bab4f --- /dev/null +++ b/util/datetime/base.h @@ -0,0 +1,823 @@ +#pragma once + +#include "systime.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(__cpp_lib_three_way_comparison) + #include +#endif + +#include +#include +#include + +#include + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable : 4244) // conversion from 'time_t' to 'long', possible loss of data +#endif // _MSC_VER + +// Microseconds since epoch +class TInstant; + +// Duration is microseconds. Could be used to store timeouts, for example. +class TDuration; + +/// Current time +static inline TInstant Now() noexcept; + +/// Use Now() method to obtain current time instead of *Seconds() unless you understand what are you doing. + +class TDateTimeParseException: public yexception { +}; + +const int DATE_BUF_LEN = 4 + 2 + 2 + 1; // [YYYYMMDD*] + +constexpr long seconds(const struct tm& theTm) { + return 60 * (60 * theTm.tm_hour + theTm.tm_min) + theTm.tm_sec; +} + +void sprint_gm_date(char* buf, time_t when, long* sec = nullptr); +bool sscan_date(const char* date, struct tm& theTm); + +const int DATE_8601_LEN = 21; // strlen("YYYY-MM-DDThh:mm:ssZ") = 20 + '\0' + +size_t FormatDate8601(char* buf, size_t len, time_t when); + +inline void sprint_date8601(char* buf, time_t when) { + buf[FormatDate8601(buf, 64, when)] = 0; +} + +bool ParseISO8601DateTimeDeprecated(const char* date, time_t& utcTime); +bool ParseISO8601DateTimeDeprecated(const char* date, size_t dateLen, time_t& utcTime); +bool ParseRFC822DateTimeDeprecated(const char* date, time_t& utcTime); +bool ParseRFC822DateTimeDeprecated(const char* date, size_t dateLen, time_t& utcTime); +bool ParseHTTPDateTimeDeprecated(const char* date, time_t& utcTime); +bool ParseHTTPDateTimeDeprecated(const char* date, size_t dateLen, time_t& utcTime); +bool ParseX509ValidityDateTimeDeprecated(const char* date, time_t& utcTime); +bool ParseX509ValidityDateTimeDeprecated(const char* date, size_t dateLen, time_t& utcTime); + +bool ParseISO8601DateTime(const char* date, time_t& utcTime); +bool ParseISO8601DateTime(const char* date, size_t dateLen, time_t& utcTime); +bool ParseRFC822DateTime(const char* date, time_t& utcTime); +bool ParseRFC822DateTime(const char* date, size_t dateLen, time_t& utcTime); +bool ParseHTTPDateTime(const char* date, time_t& utcTime); +bool ParseHTTPDateTime(const char* date, size_t dateLen, time_t& utcTime); +bool ParseX509ValidityDateTime(const char* date, time_t& utcTime); +bool ParseX509ValidityDateTime(const char* date, size_t dateLen, time_t& utcTime); + +constexpr long TVdiff(timeval r1, timeval r2) { + return (1000000 * (r2.tv_sec - r1.tv_sec) + (r2.tv_usec - r1.tv_usec)); +} + +TString Strftime(const char* format, const struct tm* tm); + +// Use functions below instead of sprint_date (check IGNIETFERRO-892 for details) +void DateToString(char* buf, const struct tm& theTm); +void DateToString(char* buf, time_t when, long* sec = nullptr); +TString DateToString(const struct tm& theTm); +TString DateToString(time_t when, long* sec = nullptr); +// Year in format "YYYY", throws an exception if year not in range [0, 9999] +TString YearToString(const struct tm& theTm); +TString YearToString(time_t when); + +template +class TTimeBase { +public: + using TValue = ui64; + +protected: + constexpr TTimeBase(const TValue& value) noexcept + : Value_(value) + { + } + +public: + constexpr TTimeBase() noexcept + : Value_(0) + { + } + + constexpr TTimeBase(const struct timeval& tv) noexcept + : Value_(tv.tv_sec * (ui64)1000000 + tv.tv_usec) + { + } + + constexpr TValue GetValue() const noexcept { + return Value_; + } + + constexpr double SecondsFloat() const noexcept { + return Value_ * (1 / 1000000.0); + } + + constexpr double MillisecondsFloat() const noexcept { + return Value_ * (1 / 1000.0); + } + + constexpr TValue MicroSeconds() const noexcept { + return Value_; + } + + constexpr TValue MilliSeconds() const noexcept { + return MicroSeconds() / 1000; + } + + constexpr TValue Seconds() const noexcept { + return MilliSeconds() / 1000; + } + + constexpr TValue Minutes() const noexcept { + return Seconds() / 60; + } + + constexpr TValue Hours() const noexcept { + return Minutes() / 60; + } + + constexpr TValue Days() const noexcept { + return Hours() / 24; + } + + constexpr TValue NanoSeconds() const noexcept { + return MicroSeconds() >= (Max() / (TValue)1000) ? Max() : MicroSeconds() * (TValue)1000; + } + + constexpr ui32 MicroSecondsOfSecond() const noexcept { + return MicroSeconds() % (TValue)1000000; + } + + constexpr ui32 MilliSecondsOfSecond() const noexcept { + return MicroSecondsOfSecond() / (TValue)1000; + } + + constexpr ui32 NanoSecondsOfSecond() const noexcept { + return MicroSecondsOfSecond() * (TValue)1000; + } + + constexpr explicit operator bool() const noexcept { + return Value_; + } + +protected: + TValue Value_; // microseconds count +}; + +namespace NDateTimeHelpers { + template + struct TPrecisionHelper { + using THighPrecision = ui64; + }; + + template <> + struct TPrecisionHelper { + using THighPrecision = double; + }; + + template <> + struct TPrecisionHelper { + using THighPrecision = double; + }; +} + +class TDuration: public TTimeBase { + using TBase = TTimeBase; + +private: + /** + * private construct from microseconds + */ + constexpr explicit TDuration(TValue value) noexcept + : TBase(value) + { + } + +public: + constexpr TDuration() noexcept { + } + + constexpr TDuration(const struct timeval& tv) noexcept + : TBase(tv) + { + } + + /** + * TDuration is compatible with std::chrono::duration: + * it can be constructed and compared with std::chrono::duration. + * But there are two significant and dangerous differences between them: + * 1) TDuration is never negative and use saturation between 0 and maximum value. + * std::chrono::duration can be negative and can overflow. + * 2) TDuration uses integer number of microseconds. + * std::chrono::duration is flexible, can be integer of floating point, + * can have different precisions. + * So when casted from std::chrono::duration to TDuration value is clamped and rounded. + * In arithmetic operations std::chrono::duration argument is only rounded, + * result is TDuration and it clamped and rounded. + * In comparisons std::chrono::duration argument is rounded. + */ + template + constexpr TDuration(std::chrono::duration duration) noexcept { + static_assert( + std::ratio_greater_equal::value && + (!std::is_floating_point::value || std::ratio_greater::value), + "Extremely likely it is loss of precision, because TDuration stores microseconds. " + "Cast you duration explicitly to microseconds if you really need it."); + + if (duration.count() < 0) { + *this = TDuration::Zero(); // clamp from the bottom + } else { + if +#if !defined(__NVCC__) + constexpr +#endif + /* if [constexpr] */ (std::ratio_greater::value || std::is_floating_point::value) { + // clamp from the top + using TCommonDuration = std::chrono::duration::type, TRatio>; + constexpr auto maxDuration = std::chrono::duration(::Max()); + if (std::chrono::duration_cast(duration) >= std::chrono::duration_cast(maxDuration)) { + *this = TDuration::Max(); + return; + } + } + const TValue us = std::chrono::duration_cast>(duration).count(); + *this = TDuration::MicroSeconds(us); + } + } + + static constexpr TDuration FromValue(TValue value) noexcept { + return TDuration(value); + } + + static constexpr TDuration MicroSeconds(ui64 us) noexcept { + return TDuration(us); + } + + /* noexcept(false) as conversion from T might throw, for example FromString("abc") */ + template + static constexpr TDuration MilliSeconds(T ms) noexcept(false) { + return MicroSeconds((ui64)(typename NDateTimeHelpers::TPrecisionHelper::THighPrecision(ms) * 1000)); + } + + using TBase::Days; + using TBase::Hours; + using TBase::MicroSeconds; + using TBase::MilliSeconds; + using TBase::Minutes; + using TBase::Seconds; + + /// DeadLineFromTimeOut + inline TInstant ToDeadLine() const; + constexpr TInstant ToDeadLine(TInstant now) const; + + static constexpr TDuration Max() noexcept { + return TDuration(::Max()); + } + + static constexpr TDuration Zero() noexcept { + return TDuration(); + } + + /* noexcept(false) as conversion from T might throw, for example FromString("abc") */ + template + static constexpr TDuration Seconds(T s) noexcept(false) { + return MilliSeconds(typename NDateTimeHelpers::TPrecisionHelper::THighPrecision(s) * 1000); + } + + static constexpr TDuration Minutes(ui64 m) noexcept { + return Seconds(m * 60); + } + + static constexpr TDuration Hours(ui64 h) noexcept { + return Minutes(h * 60); + } + + static constexpr TDuration Days(ui64 d) noexcept { + return Hours(d * 24); + } + + /// parses strings like 10s, 15ms, 15.05s, 20us, or just 25 (s). See parser_ut.cpp for details + [[nodiscard]] static TDuration Parse(const TStringBuf input); + + static bool TryParse(const TStringBuf input, TDuration& result); + + // note global Out method is defined for TDuration, so it could be written to IOutputStream as text + + template + inline TDuration& operator+=(const T& t) noexcept { + return (*this = (*this + t)); + } + + template + inline TDuration& operator-=(const T& t) noexcept { + return (*this = (*this - t)); + } + + template + inline TDuration& operator*=(const T& t) noexcept { + return (*this = (*this * t)); + } + + template + inline TDuration& operator/=(const T& t) noexcept { + return (*this = (*this / t)); + } + + TString ToString() const; +}; + +Y_DECLARE_PODTYPE(TDuration); + +template <> +struct THash { + size_t operator()(const TDuration& key) const { + return THash()(key.GetValue()); + } +}; + +/// TInstant and TDuration are guaranteed to have same precision +class TInstant: public TTimeBase { + using TBase = TTimeBase; + +private: + /** + * private construct from microseconds since epoch + */ + constexpr explicit TInstant(TValue value) noexcept + : TBase(value) + { + } + +public: + constexpr TInstant() noexcept { + } + + constexpr TInstant(const struct timeval& tv) noexcept + : TBase(tv) + { + } + + static constexpr TInstant FromValue(TValue value) noexcept { + return TInstant(value); + } + + static inline TInstant Now() { + return TInstant::MicroSeconds(::MicroSeconds()); + } + + using TBase::Days; + using TBase::Hours; + using TBase::MicroSeconds; + using TBase::MilliSeconds; + using TBase::Minutes; + using TBase::Seconds; + + static constexpr TInstant Max() noexcept { + return TInstant(::Max()); + } + + static constexpr TInstant Zero() noexcept { + return TInstant(); + } + + /// us since epoch + static constexpr TInstant MicroSeconds(ui64 us) noexcept { + return TInstant(us); + } + + /// ms since epoch + static constexpr TInstant MilliSeconds(ui64 ms) noexcept { + return MicroSeconds(ms * 1000); + } + + /// seconds since epoch + static constexpr TInstant Seconds(ui64 s) noexcept { + return MilliSeconds(s * 1000); + } + + /// minutes since epoch + static constexpr TInstant Minutes(ui64 m) noexcept { + return Seconds(m * 60); + } + + /// hours since epoch + static constexpr TInstant Hours(ui64 h) noexcept { + return Minutes(h * 60); + } + + /// days since epoch + static constexpr TInstant Days(ui64 d) noexcept { + return Hours(d * 24); + } + + constexpr time_t TimeT() const noexcept { + return (time_t)Seconds(); + } + + inline struct timeval TimeVal() const noexcept { + struct timeval tv; + ::Zero(tv); + tv.tv_sec = TimeT(); + tv.tv_usec = MicroSecondsOfSecond(); + return tv; + } + + inline struct tm* LocalTime(struct tm* tm) const noexcept { + time_t clock = Seconds(); + return localtime_r(&clock, tm); + } + + inline struct tm* GmTime(struct tm* tm) const noexcept { + time_t clock = Seconds(); + return GmTimeR(&clock, tm); + } + + /** + * Formats the instant using the UTC time zone, with microsecond precision. + * + * @returns An ISO 8601 formatted string, e.g. '2015-11-21T23:30:27.991669Z'. + * @note Global Out method is defined to TInstant, so it can be written as text to IOutputStream. + */ + TString ToString() const; + + /** + * Formats the instant using the UTC time zone. + * + * @returns An RFC822 formatted string, e.g. 'Sun, 06 Nov 1994 08:49:37 GMT'. + */ + TString ToRfc822String() const; + + /** + * Formats the instant using the UTC time zone, with second precision. + * + * @returns An ISO 8601 formatted string, e.g. '2015-11-21T23:30:27Z'. + */ + TString ToStringUpToSeconds() const; + + /** + * Formats the instant using the system time zone, with microsecond precision. + * + * @returns An ISO 8601 / RFC 3339 formatted string, + * e.g. '2015-11-22T04:30:27.991669+05:00'. + */ + TString ToIsoStringLocal() const; + + /** + * Formats the instant using the system time zone, with microsecond precision. + * + * @returns A semi-ISO 8601 formatted string with timezone without colon, + * e.g. '2015-11-22T04:30:27.991669+0500'. + */ + TString ToStringLocal() const; + + /** + * Formats the instant using the system time zone. + * + * @returns An RFC822 formatted string, e.g. 'Sun, 06 Nov 1994 08:49:37 MSK'. + */ + TString ToRfc822StringLocal() const; + + /** + * Formats the instant using the system time zone, with second precision. + * + * @returns An ISO 8601 / RFC 3339 formatted string, + * e.g. '2015-11-22T04:30:27+05:00'. + */ + TString ToIsoStringLocalUpToSeconds() const; + + /** + * Formats the instant using the system time zone, with second precision. + * + * @returns A semi-ISO 8601 formatted string with timezone without colon, + * e.g. '2015-11-22T04:30:27+0500'. + */ + TString ToStringLocalUpToSeconds() const; + + TString FormatLocalTime(const char* format) const noexcept; + TString FormatGmTime(const char* format) const noexcept; + + /// See #TryParseIso8601. + static TInstant ParseIso8601(TStringBuf); + /// See #TryParseRfc822. + static TInstant ParseRfc822(TStringBuf); + /// See #TryParseHttp. + static TInstant ParseHttp(TStringBuf); + /// See #TryParseX509. + static TInstant ParseX509Validity(TStringBuf); + + /// ISO 8601 Representation of Dates and Times + /// + /// @link https://www.iso.org/standard/40874.html Description of format. + static bool TryParseIso8601(TStringBuf input, TInstant& instant); + + /// RFC 822 Date and Time specification + /// + /// @link https://tools.ietf.org/html/rfc822#section-5 Description of format. + static bool TryParseRfc822(TStringBuf input, TInstant& instant); + + /// RFC 2616 3.3.1 Full Date + /// + /// @link https://tools.ietf.org/html/rfc2616#section-3.3.1 Description of format. + static bool TryParseHttp(TStringBuf input, TInstant& instant); + + /// X.509 certificate validity time (see rfc5280 4.1.2.5.*) + /// + /// @link https://tools.ietf.org/html/rfc5280#section-4.1.2.5 Description of format. + static bool TryParseX509(TStringBuf input, TInstant& instant); + + static TInstant ParseIso8601Deprecated(TStringBuf); + static TInstant ParseRfc822Deprecated(TStringBuf); + static TInstant ParseHttpDeprecated(TStringBuf); + static TInstant ParseX509ValidityDeprecated(TStringBuf); + + static bool TryParseIso8601Deprecated(TStringBuf input, TInstant& instant); + static bool TryParseRfc822Deprecated(TStringBuf input, TInstant& instant); + static bool TryParseHttpDeprecated(TStringBuf input, TInstant& instant); + static bool TryParseX509Deprecated(TStringBuf input, TInstant& instant); + + template + inline TInstant& operator+=(const T& t) noexcept { + return (*this = (*this + t)); + } + + template + inline TInstant& operator-=(const T& t) noexcept { + return (*this = (*this - t)); + } +}; + +Y_DECLARE_PODTYPE(TInstant); + +template <> +struct THash { + size_t operator()(const TInstant& key) const { + return THash()(key.GetValue()); + } +}; + +namespace NPrivate { + template + struct TPrintableLocalTime { + TInstant MomentToPrint; + + constexpr explicit TPrintableLocalTime(TInstant momentToPrint) + : MomentToPrint(momentToPrint) + { + } + }; +} + +/** @name Helpers for printing local times to `IOutputStream`s. + * The FormatLocal* functions create an opaque object that, when written to + * a `IOutputStream`, outputs this instant as an ISO 8601 formatted string + * using the system time zone. + * + * @note The only reason behind this set of functions is to avoid excessive + * allocations when you directly print the local time to a stream. + * + * If you need something beyond just printing the value or your code + * is not performance-critical, feel free to use the corresponding + * TInstant::ToString*() functions. + */ +///@{ +/// @see TInstant::ToIsoStringLocal() +::NPrivate::TPrintableLocalTime FormatIsoLocal(TInstant instant); +/// @see TInstant::ToStringLocal() +::NPrivate::TPrintableLocalTime FormatLocal(TInstant instant); +/// @see TInstant::ToIsoStringLocalUpToSeconds() +::NPrivate::TPrintableLocalTime FormatIsoLocalUpToSeconds(TInstant instant); +/// @see TInstant::ToStringLocalUpToSeconds() +::NPrivate::TPrintableLocalTime FormatLocalUpToSeconds(TInstant instant); +///@} + +template +static constexpr bool operator<(const TTimeBase& l, const TTimeBase& r) noexcept { + return l.GetValue() < r.GetValue(); +} + +template +static constexpr bool operator<=(const TTimeBase& l, const TTimeBase& r) noexcept { + return l.GetValue() <= r.GetValue(); +} + +template +static constexpr bool operator==(const TTimeBase& l, const TTimeBase& r) noexcept { + return l.GetValue() == r.GetValue(); +} + +template +static constexpr bool operator!=(const TTimeBase& l, const TTimeBase& r) noexcept { + return l.GetValue() != r.GetValue(); +} + +template +static constexpr bool operator>(const TTimeBase& l, const TTimeBase& r) noexcept { + return l.GetValue() > r.GetValue(); +} + +template +static constexpr bool operator>=(const TTimeBase& l, const TTimeBase& r) noexcept { + return l.GetValue() >= r.GetValue(); +} + +namespace NDateTimeHelpers { + template + static constexpr T SumWithSaturation(T a, T b) { + static_assert(!std::numeric_limits::is_signed, "expect !std::numeric_limits::is_signed"); + + return Max() - a < b ? Max() : a + b; + } + + template + static constexpr T DiffWithSaturation(T a, T b) { + static_assert(!std::numeric_limits::is_signed, "expect !std::numeric_limits::is_signed"); + + return a < b ? 0 : a - b; + } +} + +constexpr TDuration operator-(const TInstant& l, const TInstant& r) noexcept { + return TDuration::FromValue(::NDateTimeHelpers::DiffWithSaturation(l.GetValue(), r.GetValue())); +} + +constexpr TInstant operator+(const TInstant& i, const TDuration& d) noexcept { + return TInstant::FromValue(::NDateTimeHelpers::SumWithSaturation(i.GetValue(), d.GetValue())); +} + +constexpr TInstant operator-(const TInstant& i, const TDuration& d) noexcept { + return TInstant::FromValue(::NDateTimeHelpers::DiffWithSaturation(i.GetValue(), d.GetValue())); +} + +constexpr TDuration operator-(const TDuration& l, const TDuration& r) noexcept { + return TDuration::FromValue(::NDateTimeHelpers::DiffWithSaturation(l.GetValue(), r.GetValue())); +} + +constexpr TDuration operator+(const TDuration& l, const TDuration& r) noexcept { + return TDuration::FromValue(::NDateTimeHelpers::SumWithSaturation(l.GetValue(), r.GetValue())); +} + +template +constexpr bool operator==(const TDuration& l, const std::chrono::duration& r) noexcept { + return r.count() >= 0 && l == TDuration(r); +} + +#if defined(__cpp_lib_three_way_comparison) + +template +constexpr std::strong_ordering operator<=>(const TDuration& l, const std::chrono::duration& r) noexcept { + if (r.count() < 0) { + return std::strong_ordering::greater; + } + return l.GetValue() <=> TDuration(r).GetValue(); +} + +#else + +template +constexpr bool operator<(const TDuration& l, const std::chrono::duration& r) noexcept { + return r.count() >= 0 && l < TDuration(r); +} + +template +constexpr bool operator<=(const TDuration& l, const std::chrono::duration& r) noexcept { + return r.count() >= 0 && l <= TDuration(r); +} + +template +constexpr bool operator!=(const TDuration& l, const std::chrono::duration& r) noexcept { + return !(l == r); +} + +template +constexpr bool operator>(const TDuration& l, const std::chrono::duration& r) noexcept { + return r.count() < 0 || l > TDuration(r); +} + +template +constexpr bool operator>=(const TDuration& l, const std::chrono::duration& r) noexcept { + return r.count() < 0 || l >= TDuration(r); +} + +template +constexpr bool operator<(const std::chrono::duration& l, const TDuration& r) noexcept { + return r > l; +} + +template +constexpr bool operator<=(const std::chrono::duration& l, const TDuration& r) noexcept { + return r >= l; +} + +template +constexpr bool operator==(const std::chrono::duration& l, const TDuration& r) noexcept { + return r == l; +} + +template +constexpr bool operator!=(const std::chrono::duration& l, const TDuration& r) noexcept { + return r != l; +} + +template +constexpr bool operator>(const std::chrono::duration& l, const TDuration& r) noexcept { + return r < l; +} + +template +constexpr bool operator>=(const std::chrono::duration& l, const TDuration& r) noexcept { + return r >= l; +} + +#endif + +template +constexpr TDuration operator+(const TDuration& l, const std::chrono::duration& r) noexcept { + return r < r.zero() ? l - TDuration(-r) : l + TDuration(r); +} + +template +constexpr TDuration operator+(const std::chrono::duration& l, const TDuration& r) noexcept { + return r + l; +} + +template +constexpr TDuration operator-(const TDuration& l, const std::chrono::duration& r) noexcept { + return l + (-r); +} + +template +constexpr TDuration operator-(const std::chrono::duration& l, const TDuration& r) noexcept { + return TDuration(l) - r; +} + +template +constexpr TInstant operator+(const TInstant& l, const std::chrono::duration& r) noexcept { + return r < r.zero() ? l - TDuration(-r) : l + TDuration(r); +} + +template +constexpr TInstant operator-(const TInstant& l, const std::chrono::duration& r) noexcept { + return l + (-r); +} + +template +inline TDuration operator*(TDuration d, T t) noexcept { + Y_ASSERT(t >= T()); + Y_ASSERT(t == T() || Max() / t >= d.GetValue()); + return TDuration::FromValue(d.GetValue() * t); +} + +template <> +inline TDuration operator*(TDuration d, double t) noexcept { + Y_ASSERT(t >= 0 && MaxFloor() >= d.GetValue() * t); + return TDuration::FromValue(d.GetValue() * t); +} + +template <> +inline TDuration operator*(TDuration d, float t) noexcept { + return d * static_cast(t); +} + +template +inline TDuration operator*(T t, TDuration d) noexcept { + return d * t; +} + +template ::value, int> = 0> +constexpr TDuration operator/(const TDuration& d, const T& t) noexcept { + return TDuration::FromValue(d.GetValue() / t); +} + +constexpr double operator/(const TDuration& x, const TDuration& y) noexcept { + return static_cast(x.GetValue()) / static_cast(y.GetValue()); +} + +inline TInstant TDuration::ToDeadLine() const { + return ToDeadLine(TInstant::Now()); +} + +constexpr TInstant TDuration::ToDeadLine(TInstant now) const { + return now + *this; +} + +void Sleep(TDuration duration); +void SleepUntil(TInstant instant); + +static inline TInstant Now() noexcept { + return TInstant::Now(); +} + +#ifdef _MSC_VER + #pragma warning(pop) +#endif // _MSC_VER diff --git a/util/datetime/base.pxd b/util/datetime/base.pxd new file mode 100644 index 00000000000..1d863b1bd84 --- /dev/null +++ b/util/datetime/base.pxd @@ -0,0 +1,126 @@ +from libc.stdint cimport uint32_t +from libc.stdint cimport uint64_t +from libcpp cimport bool as bool_t +from posix.types cimport time_t + +from util.generic.string cimport TString, TStringBuf + + +cdef extern from "" nogil: + + cdef cppclass TTimeBase: + TTimeBase() + TTimeBase(uint64_t) + + uint64_t GetValue() + double SecondsFloat() + uint64_t MicroSeconds() + uint64_t MilliSeconds() + uint64_t Seconds() + uint64_t Minutes() + uint64_t Hours() + uint64_t Days() + uint64_t NanoSeconds() + uint32_t MicroSecondsOfSecond() + uint32_t MilliSecondsOfSecond() + uint32_t NanoSecondsOfSecond() + + + cdef cppclass TInstant(TTimeBase): + TInstant() + TInstant(uint64_t) + + @staticmethod + TInstant Now() except + + @staticmethod + TInstant Max() + @staticmethod + TInstant Zero() + @staticmethod + TInstant MicroSeconds(uint64_t) + @staticmethod + TInstant MilliSeconds(uint64_t) + @staticmethod + TInstant Seconds(uint64_t) + @staticmethod + TInstant Minutes(uint64_t) + @staticmethod + TInstant Hours(uint64_t) + @staticmethod + TInstant Days(uint64_t) + + time_t TimeT() + + TString ToString() except + + TString ToStringUpToSeconds() except + + TString ToStringLocal() except + + TString ToStringLocalUpToSeconds() except + + TString FormatLocalTime(const char*) + TString FormatGmTime(const char* format) + + @staticmethod + TInstant ParseIso8601(const TStringBuf) except + + @staticmethod + TInstant ParseRfc822(const TStringBuf) except + + @staticmethod + TInstant ParseHttp(const TStringBuf) except + + @staticmethod + TInstant ParseX509Validity(const TStringBuf) except + + + @staticmethod + bool_t TryParseIso8601(const TStringBuf, TInstant&) except + + @staticmethod + bool_t TryParseRfc822(const TStringBuf, TInstant&) except + + @staticmethod + bool_t TryParseHttp(const TStringBuf, TInstant&) except + + @staticmethod + bool_t TryParseX509(const TStringBuf, TInstant&) except + + + @staticmethod + TInstant ParseIso8601Deprecated(const TStringBuf) except + + @staticmethod + TInstant ParseRfc822Deprecated(const TStringBuf) except + + @staticmethod + TInstant ParseHttpDeprecated(const TStringBuf) except + + @staticmethod + TInstant ParseX509ValidityDeprecated(const TStringBuf) except + + + @staticmethod + bool_t TryParseIso8601Deprecated(const TStringBuf, TInstant&) except + + @staticmethod + bool_t TryParseRfc822Deprecated(const TStringBuf, TInstant&) except + + @staticmethod + bool_t TryParseHttpDeprecated(const TStringBuf, TInstant&) except + + @staticmethod + bool_t TryParseX509Deprecated(const TStringBuf, TInstant&) except + + + + cdef cppclass TDuration(TTimeBase): + TDuration() + TDuration(uint64_t) + + @staticmethod + TDuration MicroSeconds(uint64_t) + + TInstant ToDeadLine() except + + TInstant ToDeadLine(TInstant) except + + + @staticmethod + TDuration Max() + @staticmethod + TDuration Zero() + @staticmethod + TDuration Seconds(uint64_t) + @staticmethod + TDuration Minutes(uint64_t) + @staticmethod + TDuration Hours(uint64_t) + @staticmethod + TDuration Days(uint64_t) + + @staticmethod + TDuration Parse(const TStringBuf) + @staticmethod + bool_t TryParse(const TStringBuf, TDuration&) + + TString ToString() except + diff --git a/util/datetime/base_ut.cpp b/util/datetime/base_ut.cpp new file mode 100644 index 00000000000..5ada0794b70 --- /dev/null +++ b/util/datetime/base_ut.cpp @@ -0,0 +1,656 @@ +#include "base.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std::chrono_literals; + +struct TTestTime { + const time_t T_ = 987654321; + const char* Date_ = "Thu Apr 19 04:25:21 2001\n"; + const char* SprintDate_ = "20010419"; + const long SprintSecs_ = 15921; + struct tm Tm_; + TTestTime() { + Tm_.tm_sec = 21; + Tm_.tm_min = 25; + Tm_.tm_hour = 4; + Tm_.tm_mday = 19; + Tm_.tm_mon = 3; + Tm_.tm_year = 101; + Tm_.tm_wday = 4; + Tm_.tm_yday = 108; + } +}; + +namespace { + inline void OldDate8601(char* buf, size_t bufLen, time_t when) { + struct tm theTm; + struct tm* ret = nullptr; + + ret = GmTimeR(&when, &theTm); + + if (ret) { + snprintf(buf, bufLen, "%04d-%02d-%02dT%02d:%02d:%02dZ", theTm.tm_year + 1900, theTm.tm_mon + 1, theTm.tm_mday, theTm.tm_hour, theTm.tm_min, theTm.tm_sec); + } else { + *buf = '\0'; + } + } +} + +Y_UNIT_TEST_SUITE(TestSprintDate) { + Y_UNIT_TEST(Year9999) { + struct tm t; + t.tm_year = 9999 - 1900; + t.tm_mday = 1; + t.tm_mon = 10; + + char buf[DATE_BUF_LEN]; + DateToString(buf, t); + + TString expectedDate = "99991101"; + + UNIT_ASSERT_VALUES_EQUAL(expectedDate, ToString(buf)); + } + Y_UNIT_TEST(YearAfter9999) { + struct tm t; + t.tm_year = 123456 - 1900; + t.tm_mday = 1; + t.tm_mon = 10; + + char buf[DATE_BUF_LEN]; + UNIT_ASSERT_EXCEPTION(DateToString(buf, t), yexception); + } + Y_UNIT_TEST(SmallYear) { + struct tm t; + t.tm_year = 0 - 1900; + t.tm_mday = 1; + t.tm_mon = 10; + + char buf[DATE_BUF_LEN]; + DateToString(buf, t); + + const TString expectedDate = TString("00001101"); + + UNIT_ASSERT_VALUES_EQUAL(expectedDate, ToString(buf)); + } + Y_UNIT_TEST(SmallYearAndMonth) { + struct tm t; + t.tm_year = 99 - 1900; + t.tm_mday = 1; + t.tm_mon = 0; + + char buf[DATE_BUF_LEN]; + DateToString(buf, t); + + const TString expectedDate = TString("00990101"); + + UNIT_ASSERT_VALUES_EQUAL(expectedDate, ToString(buf)); + } + Y_UNIT_TEST(FromZeroTimestamp) { + const time_t timestamp = 0; + + char buf[DATE_BUF_LEN]; + DateToString(buf, timestamp); + + const TString expectedDate = TString("19700101"); + + UNIT_ASSERT_VALUES_EQUAL(expectedDate, ToString(buf)); + } + Y_UNIT_TEST(FromTimestamp) { + const time_t timestamp = 1524817858; + + char buf[DATE_BUF_LEN]; + DateToString(buf, timestamp); + + const TString expectedDate = TString("20180427"); + + UNIT_ASSERT_VALUES_EQUAL(expectedDate, ToString(buf)); + } + Y_UNIT_TEST(FromTimestampAsTString) { + const time_t timestamp = 1524817858; + + const TString expectedDate = TString("20180427"); + + UNIT_ASSERT_VALUES_EQUAL(expectedDate, DateToString(timestamp)); + } + Y_UNIT_TEST(YearToString) { + struct tm t; + t.tm_year = 99 - 1900; + t.tm_mday = 1; + t.tm_mon = 0; + + TString expectedYear = TString("0099"); + + UNIT_ASSERT_VALUES_EQUAL(expectedYear, YearToString(t)); + } + Y_UNIT_TEST(YearToStringBigYear) { + struct tm t; + t.tm_year = 123456 - 1900; + t.tm_mday = 1; + t.tm_mon = 0; + + UNIT_ASSERT_EXCEPTION(YearToString(t), yexception); + } + Y_UNIT_TEST(YearToStringAsTimestamp) { + const time_t timestamp = 1524817858; + + const TString expectedYear = TString("2018"); + + UNIT_ASSERT_VALUES_EQUAL(expectedYear, YearToString(timestamp)); + } +} + +Y_UNIT_TEST_SUITE(TDateTimeTest) { + Y_UNIT_TEST(Test8601) { + char buf1[100]; + char buf2[100]; + + for (size_t i = 0; i < 1000000; ++i) { + const time_t t = RandomNumber(); + + OldDate8601(buf1, sizeof(buf1), t); + sprint_date8601(buf2, t); + + UNIT_ASSERT_VALUES_EQUAL(TStringBuf(buf1), TStringBuf(buf2)); + } + } + + inline bool CompareTM(const struct tm& a, const struct tm& b) { + return ( + a.tm_sec == b.tm_sec && + a.tm_min == b.tm_min && + a.tm_hour == b.tm_hour && + a.tm_mday == b.tm_mday && + a.tm_mon == b.tm_mon && + a.tm_year == b.tm_year && + a.tm_wday == b.tm_wday && + a.tm_yday == b.tm_yday); + } + + static inline TString Str(const struct tm& a) { + return TStringBuilder() << "(" + << a.tm_sec << ", " + << a.tm_min << ", " + << a.tm_hour << ", " + << a.tm_mday << ", " + << a.tm_mon << ", " + << a.tm_year << ", " + << a.tm_wday << ", " +#if !defined(_musl_) && !defined(_win_) + << a.tm_yday +#endif + << ")"; + } + + Y_UNIT_TEST(TestBasicFuncs) { + ui64 mlsecB = millisec(); + ui64 mcrsecB = MicroSeconds(); + struct timeval tvB; + gettimeofday(&tvB, nullptr); + + usleep(100000); + + ui64 mlsecA = millisec(); + ui64 mcrsecA = MicroSeconds(); + struct timeval tvA; + gettimeofday(&tvA, nullptr); + + UNIT_ASSERT(mlsecB + 90 < mlsecA); + UNIT_ASSERT((mcrsecB + 90000 < mcrsecA)); + //UNIT_ASSERT(ToMicroSeconds(&tvB) + 90000 < ToMicroSeconds(&tvA)); + //UNIT_ASSERT(TVdiff(tvB, tvA) == long(ToMicroSeconds(&tvA) - ToMicroSeconds(&tvB))); + } + + Y_UNIT_TEST(TestCompatFuncs) { + struct tm t; + struct tm* tret = nullptr; + TTestTime e; + tret = gmtime_r(&e.T_, &t); + UNIT_ASSERT(tret == &t); + UNIT_ASSERT(CompareTM(e.Tm_, t)); + + /* + * strptime seems to be broken on Mac OS X: + * + * struct tm t; + * char *ret = strptime("Jul", "%b ", &t); + * printf("-%s-\n", ret); + * + * yields "- -": ret contains a pointer to a substring of the format string, + * that should never occur: function returns either NULL or pointer to buf substring. + * + * So this test fails on Mac OS X. + */ + + struct tm t2; + Zero(t2); + char* ret = strptime(e.Date_, "%a %b %d %H:%M:%S %Y\n ", &t2); + UNIT_ASSERT(ret == e.Date_ + strlen(e.Date_)); + UNIT_ASSERT_VALUES_EQUAL(Str(e.Tm_), Str(t2)); + time_t t3 = timegm(&t); + UNIT_ASSERT(t3 == e.T_); + } + + Y_UNIT_TEST(TestSprintSscan) { + char buf[256]; + long secs; + TTestTime e; + + sprint_gm_date(buf, e.T_, &secs); + UNIT_ASSERT(strcmp(buf, e.SprintDate_) == 0); + UNIT_ASSERT(secs == e.SprintSecs_); + + struct tm t; + Zero(t); + bool ret = sscan_date(buf, t); + UNIT_ASSERT(ret); + UNIT_ASSERT( + t.tm_year == e.Tm_.tm_year && + t.tm_mon == e.Tm_.tm_mon && + t.tm_mday == e.Tm_.tm_mday); + + buf[0] = 'a'; + ret = sscan_date(buf, t); + UNIT_ASSERT(!ret); + } + + Y_UNIT_TEST(TestNow) { + i64 seconds = Seconds(); + i64 milliseconds = millisec(); + i64 microseconds = MicroSeconds(); + UNIT_ASSERT(Abs(seconds - milliseconds / 1000) <= 1); + UNIT_ASSERT(Abs(milliseconds - microseconds / 1000) < 100); + UNIT_ASSERT(seconds > 1243008607); // > time when test was written + } + + Y_UNIT_TEST(TestStrftime) { + struct tm tm; + Zero(tm); + tm.tm_year = 109; + tm.tm_mon = 4; + tm.tm_mday = 29; + UNIT_ASSERT_STRINGS_EQUAL("2009-05-29", Strftime("%Y-%m-%d", &tm)); + } + + Y_UNIT_TEST(TestNanoSleep) { + NanoSleep(0); + NanoSleep(1); + NanoSleep(1000); + NanoSleep(1000000); + } + + static bool TimeZoneEq(const char* zone0, const char* zone1) { + if (strcmp(zone0, "GMT") == 0) { + zone0 = "UTC"; + } + if (strcmp(zone1, "GMT") == 0) { + zone1 = "UTC"; + } + return strcmp(zone0, zone1) == 0; + } + + static bool CompareTMFull(const tm* t0, const tm* t1) { + return t0 && t1 && + CompareTM(*t0, *t1) && + (t0->tm_isdst == t1->tm_isdst) +#ifndef _win_ + && (t0->tm_gmtoff == t1->tm_gmtoff) && + TimeZoneEq(t0->tm_zone, t1->tm_zone) +#endif // _win_ + && true; + } + + Y_UNIT_TEST(TestGmTimeR) { + time_t starttime = static_cast(Max(-12244089600LL, Min())); // 1-Jan-1582 + time_t finishtime = static_cast(Min(0xFFFFFFFF * 20, Max())); + time_t step = (finishtime - starttime) / 25; + struct tm tms0, tms1; + struct tm* ptm0 = nullptr; + struct tm* ptm1 = nullptr; + for (time_t t = starttime; t < finishtime; t += step) { + ptm0 = GmTimeR(&t, &tms0); + UNIT_ASSERT_EQUAL(ptm0, &tms0); + +#ifdef _win_ + if (tms0.tm_year + 1900 > 3000) { + // Windows: _MAX__TIME64_T == 23:59:59. 12/31/3000 UTC + continue; + } +#endif + + ptm1 = gmtime_r(&t, &tms1); + if (!ptm1) { + continue; + } + UNIT_ASSERT_EQUAL(ptm1, &tms1); + UNIT_ASSERT(CompareTMFull(ptm0, ptm1)); + } + } +} + +Y_UNIT_TEST_SUITE(DateTimeTest) { + Y_UNIT_TEST(TestDurationFromFloat) { + UNIT_ASSERT_EQUAL(TDuration::MilliSeconds(500), TDuration::Seconds(0.5)); + UNIT_ASSERT_EQUAL(TDuration::MilliSeconds(500), TDuration::Seconds(0.5f)); + } + + Y_UNIT_TEST(TestSecondsLargeValue) { + unsigned int seconds = UINT_MAX; + UNIT_ASSERT_VALUES_EQUAL(((ui64)seconds) * 1000000, TDuration::Seconds(seconds).MicroSeconds()); + } + + Y_UNIT_TEST(TestToString) { +#define CHECK_CONVERTIBLE(v) \ + do { \ + UNIT_ASSERT_VALUES_EQUAL(v, ToString(TDuration::Parse(v))); \ + UNIT_ASSERT_VALUES_EQUAL(v, TDuration::Parse(v).ToString()); \ + } while (0) +#if 0 + + CHECK_CONVERTIBLE("10s"); + CHECK_CONVERTIBLE("1234s"); + CHECK_CONVERTIBLE("1234ms"); + CHECK_CONVERTIBLE("12ms"); + CHECK_CONVERTIBLE("12us"); + CHECK_CONVERTIBLE("1234us"); +#endif + + CHECK_CONVERTIBLE("1.000000s"); + CHECK_CONVERTIBLE("11234.000000s"); + CHECK_CONVERTIBLE("0.011122s"); + CHECK_CONVERTIBLE("33.011122s"); + } + + Y_UNIT_TEST(TestFromString) { + static const struct T { + const char* const Str; + const TDuration::TValue MicroSeconds; + const bool Parseable; + } tests[] = { + {"0", 0, true}, + {"1", 1000000, true}, + {"2s", 2000000, true}, + {"3ms", 3000, true}, + {"x3ms", 0, false}, + }; + + for (const T* t = tests; t != std::end(tests); ++t) { + // FromString + bool parsed = false; + try { + TDuration time = FromString(t->Str); + parsed = true; + UNIT_ASSERT_EQUAL(t->MicroSeconds, time.MicroSeconds()); + } catch (const yexception&) { + UNIT_ASSERT_VALUES_EQUAL(parsed, t->Parseable); + } + // TryFromString + TDuration tryTime; + UNIT_ASSERT_VALUES_EQUAL(TryFromString(t->Str, tryTime), t->Parseable); + if (t->Parseable) { + UNIT_ASSERT_EQUAL(t->MicroSeconds, tryTime.MicroSeconds()); + } + } + } + + Y_UNIT_TEST(TestSleep) { + // check does not throw + Sleep(TDuration::Seconds(0)); + Sleep(TDuration::MicroSeconds(1)); + Sleep(TDuration::MilliSeconds(1)); + } + + Y_UNIT_TEST(TestInstantToString) { + UNIT_ASSERT_VALUES_EQUAL(TString("2009-08-06T15:19:06.023455Z"), ToString(TInstant::Seconds(1249571946) + TDuration::MicroSeconds(23455))); + UNIT_ASSERT_VALUES_EQUAL(TString("2022-08-23T13:04:43.023455Z"), ToString(TInstant::Seconds(1661259883) + TDuration::MicroSeconds(23455))); + UNIT_ASSERT_VALUES_EQUAL(TString("2122-11-23T15:12:26.023455Z"), ToString(TInstant::Seconds(4824889946) + TDuration::MicroSeconds(23455))); + UNIT_ASSERT_VALUES_EQUAL(TString("2009-08-06T15:19:06.023455Z"), (TInstant::Seconds(1249571946) + TDuration::MicroSeconds(23455)).ToString()); + UNIT_ASSERT_VALUES_EQUAL(TString("2009-08-06T15:19:06Z"), (TInstant::Seconds(1249571946) + TDuration::MicroSeconds(23455)).ToStringUpToSeconds()); + } + + Y_UNIT_TEST(TestInstantToRfc822String) { + UNIT_ASSERT_VALUES_EQUAL(TString("Thu, 06 Aug 2009 15:19:06 GMT"), (TInstant::Seconds(1249571946) + TDuration::MicroSeconds(23455)).ToRfc822String()); + } + + Y_UNIT_TEST(TestInstantMath) { + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(1719), TInstant::Seconds(1700) + TDuration::Seconds(19)); + // overflow + UNIT_ASSERT_VALUES_EQUAL(TInstant::Max(), TInstant::Max() - TDuration::Seconds(10) + TDuration::Seconds(19)); + // underflow + UNIT_ASSERT_VALUES_EQUAL(TInstant::Zero(), TInstant::Seconds(1000) - TDuration::Seconds(2000)); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Zero(), TInstant::Seconds(1000) - TInstant::Seconds(2000)); + } + + Y_UNIT_TEST(TestDurationMath) { + TDuration empty; + UNIT_ASSERT(!empty); + // ensure that this compiles too + if (empty) { + UNIT_ASSERT(false); + } + TDuration nonEmpty = TDuration::MicroSeconds(1); + UNIT_ASSERT(nonEmpty); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(110), TDuration::Seconds(77) + TDuration::Seconds(33)); + // overflow + UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(), TDuration::Max() - TDuration::Seconds(1) + TDuration::Seconds(10)); + // underflow + UNIT_ASSERT_VALUES_EQUAL(TDuration::Zero(), TDuration::Seconds(20) - TDuration::Seconds(200)); + // division + UNIT_ASSERT_DOUBLES_EQUAL(TDuration::Minutes(1) / TDuration::Seconds(10), 6.0, 1e-9); + } + + Y_UNIT_TEST(TestDurationGetters) { + const TDuration value = TDuration::MicroSeconds(1234567); + UNIT_ASSERT_VALUES_EQUAL(value.Seconds(), 1); + UNIT_ASSERT_DOUBLES_EQUAL(value.SecondsFloat(), 1.234567, 1e-9); + + UNIT_ASSERT_VALUES_EQUAL(value.MilliSeconds(), 1234); + UNIT_ASSERT_DOUBLES_EQUAL(value.MillisecondsFloat(), 1234.567, 1e-9); + + UNIT_ASSERT_VALUES_EQUAL(value.MicroSeconds(), 1234567); + } + + template + void TestTimeUnits() { + T withTime = T::MicroSeconds(1249571946000000L); + T onlyMinutes = T::MicroSeconds(1249571940000000L); + T onlyHours = T::MicroSeconds(1249570800000000L); + T onlyDays = T::MicroSeconds(1249516800000000L); + ui64 minutes = 20826199; + ui64 hours = 347103; + ui64 days = 14462; + + UNIT_ASSERT_VALUES_EQUAL(withTime.Minutes(), minutes); + UNIT_ASSERT_VALUES_EQUAL(onlyMinutes, T::Minutes(minutes)); + UNIT_ASSERT_VALUES_EQUAL(onlyMinutes.Minutes(), minutes); + + UNIT_ASSERT_VALUES_EQUAL(withTime.Hours(), hours); + UNIT_ASSERT_VALUES_EQUAL(onlyMinutes.Hours(), hours); + UNIT_ASSERT_VALUES_EQUAL(onlyHours, T::Hours(hours)); + UNIT_ASSERT_VALUES_EQUAL(onlyHours.Hours(), hours); + + UNIT_ASSERT_VALUES_EQUAL(withTime.Days(), days); + UNIT_ASSERT_VALUES_EQUAL(onlyHours.Days(), days); + UNIT_ASSERT_VALUES_EQUAL(onlyDays, T::Days(days)); + UNIT_ASSERT_VALUES_EQUAL(onlyDays.Days(), days); + } + + Y_UNIT_TEST(TestInstantUnits) { + TestTimeUnits(); + } + + Y_UNIT_TEST(TestDurationUnits) { + TestTimeUnits(); + } + + Y_UNIT_TEST(TestNoexceptConstruction) { + UNIT_ASSERT_EXCEPTION(TDuration::MilliSeconds(FromString(TStringBuf("not a number"))), yexception); + UNIT_ASSERT_EXCEPTION(TDuration::Seconds(FromString(TStringBuf("not a number"))), yexception); + } + + Y_UNIT_TEST(TestFromValueForTDuration) { + // check that FromValue creates the same TDuration + TDuration d1 = TDuration::MicroSeconds(12345); + TDuration d2 = TDuration::FromValue(d1.GetValue()); + + UNIT_ASSERT_VALUES_EQUAL(d1, d2); + } + + Y_UNIT_TEST(TestFromValueForTInstant) { + // check that FromValue creates the same TInstant + TInstant i1 = TInstant::MicroSeconds(12345); + TInstant i2 = TInstant::FromValue(i1.GetValue()); + + UNIT_ASSERT_VALUES_EQUAL(i1, i2); + } + + Y_UNIT_TEST(TestTimeGmDateConversion) { + tm time{}; + time_t timestamp = 0; + + // Check all days till year 2106 (max year representable if time_t is 32 bit) + while (time.tm_year < 2106 - 1900) { + timestamp += 86400; + + GmTimeR(×tamp, &time); + time_t newTimestamp = TimeGM(&time); + + UNIT_ASSERT_VALUES_EQUAL_C( + newTimestamp, + timestamp, + "incorrect date " << (1900 + time.tm_year) << "-" << (time.tm_mon + 1) << "-" << time.tm_mday); + } + } + + Y_UNIT_TEST(TestTDurationConstructorFromStdChronoDuration) { + + UNIT_ASSERT_VALUES_EQUAL(TDuration::Zero(), TDuration(0ms)); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(42), TDuration(42us)); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(42000000000000L), TDuration(42000000000000us)); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(42), TDuration(42ms)); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(42.75), TDuration(42.75ms)); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(42), TDuration(42s)); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(42.25), TDuration(42.25s)); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Minutes(42), TDuration(42min)); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Hours(42), TDuration(42h)); + + // TDuration doesn't support negative durations + UNIT_ASSERT_VALUES_EQUAL(TDuration::Zero(), TDuration(-5min)); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(5), TDuration(std::chrono::duration{5ms})); + +#if defined(_LIBCPP_STD_VER) && _LIBCPP_STD_VER > 17 + // libstdc++ does not provide std::chrono::days at the time + // Consider removing this code upon OS_SDK update + UNIT_ASSERT_VALUES_EQUAL(TDuration::Days(1), TDuration(std::chrono::days{1})); +#endif + + // clump + UNIT_ASSERT_VALUES_EQUAL(TDuration::Zero(), TDuration(std::chrono::duration{-1})); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Zero(), TDuration(std::chrono::duration{-1})); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(), + TDuration(std::chrono::duration>{static_cast(1e18)})); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(), + TDuration(std::chrono::duration{static_cast(::Max() / 1000)})); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(), + TDuration(std::chrono::duration>{1e18})); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(), + TDuration(std::chrono::duration{::Max() / 1000})); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(), TDuration(std::chrono::duration{ + static_cast(::Max()) / 1000 + 0.1})); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(), TDuration(std::chrono::duration{ + static_cast(::Max()) / 1000 + 0.1})); + } + + Y_UNIT_TEST(TestTDurationCompareWithStdChronoDuration) { + UNIT_ASSERT(TDuration::Zero() == 0ms); + UNIT_ASSERT(TDuration::Seconds(42) == 42s); + + UNIT_ASSERT(0ms == TDuration::Zero()); + + UNIT_ASSERT(TDuration::Zero() != 1ms); + UNIT_ASSERT(TDuration::Zero() != -1ms); + UNIT_ASSERT(TDuration::MilliSeconds(1) != -1ms); + UNIT_ASSERT(TDuration::MilliSeconds(1) != -1ms); + + UNIT_ASSERT(1ms != TDuration::Zero()); + + UNIT_ASSERT(TDuration::Seconds(2) < 3s); + UNIT_ASSERT(3s > TDuration::Seconds(2)); + UNIT_ASSERT(!(TDuration::Seconds(2) < 1s)); + UNIT_ASSERT(!(TDuration::Seconds(2) < -3s)); + UNIT_ASSERT(!(TDuration::Seconds(2) < 2s)); + + UNIT_ASSERT(2s < TDuration::Seconds(3)); + + UNIT_ASSERT(TDuration::Seconds(2) <= 3s); + UNIT_ASSERT(!(TDuration::Seconds(2) <= 1s)); + UNIT_ASSERT(!(TDuration::Seconds(2) <= -3s)); + UNIT_ASSERT(TDuration::Seconds(2) <= 2s); + + UNIT_ASSERT(2s <= TDuration::Seconds(2)); + + UNIT_ASSERT(TDuration::Seconds(2) > -2s); + UNIT_ASSERT(TDuration::Seconds(2) > 1s); + UNIT_ASSERT(TDuration::Seconds(2) > 0s); + UNIT_ASSERT(!(TDuration::Seconds(2) > 3s)); + UNIT_ASSERT(!(TDuration::Seconds(2) > 2s)); + + UNIT_ASSERT(2s > TDuration::Seconds(1)); + + UNIT_ASSERT(TDuration::Seconds(2) >= -2s); + UNIT_ASSERT(TDuration::Seconds(2) >= 1s); + UNIT_ASSERT(TDuration::Seconds(2) >= 0s); + UNIT_ASSERT(!(TDuration::Seconds(2) >= 3s)); + UNIT_ASSERT(TDuration::Seconds(2) >= 2s); + + UNIT_ASSERT(2s >= TDuration::Seconds(2)); + + static_assert(TDuration::Zero() == 0ms); + static_assert(TDuration::Zero() < 1ms); + } + + Y_UNIT_TEST(TestAdditionOfStdChronoDuration) { + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(1) + 2s, TDuration::Seconds(3)); + UNIT_ASSERT_VALUES_EQUAL(2s + TDuration::Seconds(1), TDuration::Seconds(3)); + UNIT_ASSERT_VALUES_EQUAL(-2s + TDuration::Seconds(3), TDuration::Seconds(1)); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(3) + (-2s), TDuration::Seconds(1)); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(3) - 2s, TDuration::Seconds(1)); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(1) - (-2s), TDuration::Seconds(3)); + UNIT_ASSERT_VALUES_EQUAL(3s - TDuration::Seconds(2), TDuration::Seconds(1)); + UNIT_ASSERT_VALUES_EQUAL(3s - TDuration::Seconds(4), TDuration::Zero()); + + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(1) + 2s, TInstant::Seconds(3)); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(3) + (-2s), TInstant::Seconds(1)); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(3) - 2s, TInstant::Seconds(1)); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(1) - (-2s), TInstant::Seconds(3)); + + // Operations between TDuration/TInstant and std::chrono::duration are performed + // with saturation according to the rules of TDuration/TInstant + UNIT_ASSERT_VALUES_EQUAL(TDuration::Max() + 1h, TDuration::Max()); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Max() + 1h, TInstant::Max()); + UNIT_ASSERT_VALUES_EQUAL(1h + TDuration::Max(), TDuration::Max()); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Max() + 1h, TInstant::Max()); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::Max() - (-1h), TDuration::Max()); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Max() - (-1h), TInstant::Max()); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Max() - (-1h), TInstant::Max()); + + UNIT_ASSERT_VALUES_EQUAL(-1h - TDuration::Max(), TDuration::Zero()); + UNIT_ASSERT_VALUES_EQUAL(1h - TDuration::Max(), TDuration::Zero()); + + static_assert(TDuration::Zero() + 1s == 1s); + static_assert(TInstant::Seconds(1) + 1s == TInstant::Seconds(2)); + } +} diff --git a/util/datetime/benchmark/format/main.cpp b/util/datetime/benchmark/format/main.cpp new file mode 100644 index 00000000000..9c930d47140 --- /dev/null +++ b/util/datetime/benchmark/format/main.cpp @@ -0,0 +1,59 @@ +#include + +#include +#include + +class TTimestampGenerator { +public: + TInstant operator()() { + TInstant result = TInstant::MicroSeconds(Base_ + Current_); + Current_ = (Current_ + Step_) % Range_; + return result; + } + +private: + static constexpr ui64 Step_ = TDuration::MicroSeconds(1234567891011).MicroSeconds(); + static constexpr ui64 Range_ = 1ull << 45; // ~ year + static constexpr ui64 Base_ = TInstant::Seconds(1605320321).MicroSeconds(); + ui64 Current_ = 0; +}; + +Y_FORCE_INLINE static void BenchFormatStream(auto&& formatFn, benchmark::State& state) { + TTimestampGenerator gen; + TStringStream ss; + for (auto _ : state) { + ss << formatFn(gen()); + Y_DO_NOT_OPTIMIZE_AWAY(ss.Str()); + ss.clear(); + } +} + +Y_FORCE_INLINE static void BenchToString(auto&& toStringFn, benchmark::State& state) { + TTimestampGenerator gen; + TString s; + for (auto _ : state) { + s = toStringFn(gen()); + Y_DO_NOT_OPTIMIZE_AWAY(s); + } +} + +void BM_FormatIsoLocal(benchmark::State& state) { + BenchFormatStream(FormatIsoLocal, state); +} + +void BM_FormatLocal(benchmark::State& state) { + BenchFormatStream(FormatLocal, state); +} + +void BM_ToStringIsoLocal(benchmark::State& state) { + BenchToString(std::mem_fn(&TInstant::ToIsoStringLocal), state); +} + +void BM_ToStringLocal(benchmark::State& state) { + BenchToString(std::mem_fn(&TInstant::ToIsoStringLocal), state); +} + +BENCHMARK(BM_FormatIsoLocal); +BENCHMARK(BM_FormatLocal); +BENCHMARK(BM_ToStringIsoLocal); +BENCHMARK(BM_ToStringLocal); diff --git a/util/datetime/benchmark/format/ya.make b/util/datetime/benchmark/format/ya.make new file mode 100644 index 00000000000..3279e3a35ec --- /dev/null +++ b/util/datetime/benchmark/format/ya.make @@ -0,0 +1,10 @@ +G_BENCHMARK() + +PEERDIR( +) + +SRCS( + main.cpp +) + +END() diff --git a/util/datetime/benchmark/gmtime_r/main.cpp b/util/datetime/benchmark/gmtime_r/main.cpp new file mode 100644 index 00000000000..a3356d780c3 --- /dev/null +++ b/util/datetime/benchmark/gmtime_r/main.cpp @@ -0,0 +1,44 @@ +#include + +#include +#include + +void BM_GmTimeR(benchmark::State& state) { + time_t now = TInstant::Now().TimeT(); + struct tm buf {}; + + for (auto _ : state) { + Y_DO_NOT_OPTIMIZE_AWAY(GmTimeR(&now, &buf)); + } +} + +void BM_gmtime_r(benchmark::State& state) { + time_t now = TInstant::Now().TimeT(); + struct tm buf {}; + + for (auto _ : state) { + Y_DO_NOT_OPTIMIZE_AWAY(gmtime_r(&now, &buf)); + } +} + +void BM_GmTimeRRandom(benchmark::State& state, TDuration window) { + time_t now = TInstant::Now().TimeT(); + struct tm buf {}; + + TFastRng rng(2); + const size_t range = window.Seconds(); + for (auto _ : state) { + size_t offset = rng.GenRand() % range; + time_t v = now - offset; + Y_DO_NOT_OPTIMIZE_AWAY(GmTimeR(&v, &buf)); + } +} + +BENCHMARK(BM_GmTimeR); +BENCHMARK(BM_gmtime_r); +BENCHMARK_CAPTURE(BM_GmTimeRRandom, last_hour, TDuration::Hours(1)); +BENCHMARK_CAPTURE(BM_GmTimeRRandom, last_day, TDuration::Days(1)); +BENCHMARK_CAPTURE(BM_GmTimeRRandom, last_mount, TDuration::Days(31)); +BENCHMARK_CAPTURE(BM_GmTimeRRandom, last_year, TDuration::Days(365)); +BENCHMARK_CAPTURE(BM_GmTimeRRandom, last_decade, TDuration::Days(3653)); +BENCHMARK_CAPTURE(BM_GmTimeRRandom, last_half_centry, TDuration::Days(18262)); diff --git a/util/datetime/benchmark/gmtime_r/ya.make b/util/datetime/benchmark/gmtime_r/ya.make new file mode 100644 index 00000000000..1a6c0c5ef18 --- /dev/null +++ b/util/datetime/benchmark/gmtime_r/ya.make @@ -0,0 +1,11 @@ +G_BENCHMARK() + +PEERDIR( + util/draft +) + +SRCS( + main.cpp +) + +END() diff --git a/util/datetime/benchmark/ya.make b/util/datetime/benchmark/ya.make new file mode 100644 index 00000000000..fff04434980 --- /dev/null +++ b/util/datetime/benchmark/ya.make @@ -0,0 +1,4 @@ +RECURSE( + format + gmtime_r +) diff --git a/util/datetime/constants.cpp b/util/datetime/constants.cpp new file mode 100644 index 00000000000..04a2aa68f2d --- /dev/null +++ b/util/datetime/constants.cpp @@ -0,0 +1 @@ +#include "constants.h" diff --git a/util/datetime/constants.h b/util/datetime/constants.h new file mode 100644 index 00000000000..352403270e4 --- /dev/null +++ b/util/datetime/constants.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +const time_t SECONDS_IN_DAY = 86400; +const time_t SECONDS_IN_HOUR = 3600; +const time_t SECONDS_IN_HALFHOUR = 1800; diff --git a/util/datetime/cputimer.cpp b/util/datetime/cputimer.cpp new file mode 100644 index 00000000000..32ccfd4740e --- /dev/null +++ b/util/datetime/cputimer.cpp @@ -0,0 +1,150 @@ +#include "cputimer.h" + +#include +#include +#include +#include +#include + +#if defined(_unix_) + #include + #include +#elif defined(_win_) + #include +#endif + +TTimer::TTimer(const TStringBuf message) { + static const int SMALL_DURATION_CHAR_LENGTH = 9; // strlen("0.123456s") + Message_.Reserve(message.length() + SMALL_DURATION_CHAR_LENGTH + 1); // +"\n" + Message_ << message; + // Do not measure the allocations above. + Start_ = TInstant::Now(); +} + +TTimer::~TTimer() { + const TDuration duration = TInstant::Now() - Start_; + Message_ << duration << "\n"; + Cerr << Message_.Str(); +} + +static ui64 ManuallySetCyclesPerSecond = 0; + +static ui64 GetCyclesPerSecond() { + if (ManuallySetCyclesPerSecond != 0) { + return ManuallySetCyclesPerSecond; + } else { + return NHPTimer::GetCyclesPerSecond(); + } +} + +void SetCyclesPerSecond(ui64 cycles) { + ManuallySetCyclesPerSecond = cycles; +} + +ui64 GetCyclesPerMillisecond() { + return GetCyclesPerSecond() / 1000; +} + +TDuration CyclesToDuration(ui64 cycles) { + return TDuration::MicroSeconds(cycles * 1000000 / GetCyclesPerSecond()); +} + +TDuration CyclesToDurationSafe(ui64 cycles) +{ + constexpr ui64 cyclesLimit = std::numeric_limits::max() / 1000000; + if (cycles <= cyclesLimit) { + return CyclesToDuration(cycles); + } + return TDuration::MicroSeconds(cycles / GetCyclesPerSecond() * 1000000); +} + +ui64 DurationToCycles(TDuration duration) { + return duration.MicroSeconds() * GetCyclesPerSecond() / 1000000; +} + +ui64 DurationToCyclesSafe(TDuration duration) +{ + if (duration.MicroSeconds() <= std::numeric_limits::max() / GetCyclesPerSecond()) { + return DurationToCycles(duration); + } + return duration.MicroSeconds() / 1000000 * GetCyclesPerSecond(); +} + +TPrecisionTimer::TPrecisionTimer() + : Start(::GetCycleCount()) +{ +} + +ui64 TPrecisionTimer::GetCycleCount() const { + return ::GetCycleCount() - Start; +} + +TString FormatCycles(ui64 cycles) { + ui64 milliseconds = cycles / GetCyclesPerMillisecond(); + ui32 ms = ui32(milliseconds % 1000); + milliseconds /= 1000; + ui32 secs = ui32(milliseconds % 60); + milliseconds /= 60; + ui32 mins = ui32(milliseconds); + TString result; + sprintf(result, "%" PRIu32 " m %.2" PRIu32 " s %.3" PRIu32 " ms", mins, secs, ms); + return result; +} + +TFormattedPrecisionTimer::TFormattedPrecisionTimer(const char* message, IOutputStream* out) + : Message(message) + , Out(out) +{ + Start = GetCycleCount(); +} + +TFormattedPrecisionTimer::~TFormattedPrecisionTimer() { + const ui64 end = GetCycleCount(); + const ui64 diff = end - Start; + + *Out << Message << ": " << diff << " ticks " << FormatCycles(diff) << Endl; +} + +TFuncTimer::TFuncTimer(const char* func) + : Start_(TInstant::Now()) + , Func_(func) +{ + Cerr << "enter " << Func_ << Endl; +} + +TFuncTimer::~TFuncTimer() { + Cerr << "leave " << Func_ << " -> " << (TInstant::Now() - Start_) << Endl; +} + +TTimeLogger::TTimeLogger(const TString& message, bool verbose) + : Message(message) + , Verbose(verbose) + , OK(false) + , Begin(time(nullptr)) + , BeginCycles(GetCycleCount()) +{ + if (Verbose) { + fprintf(stderr, "=========================================================\n"); + fprintf(stderr, "%s started: %.24s (%lu) (%d)\n", Message.data(), ctime(&Begin), (unsigned long)Begin, (int)getpid()); + } +} + +double TTimeLogger::ElapsedTime() const { + return time(nullptr) - Begin; +} + +void TTimeLogger::SetOK() { + OK = true; +} + +TTimeLogger::~TTimeLogger() { + time_t tim = time(nullptr); + ui64 endCycles = GetCycleCount(); + if (Verbose) { + const char* prefix = (OK) ? "" : "!"; + fprintf(stderr, "%s%s ended: %.24s (%lu) (%d) (took %lus = %s)\n", + prefix, Message.data(), ctime(&tim), (unsigned long)tim, (int)getpid(), + (unsigned long)tim - (unsigned long)Begin, FormatCycles(endCycles - BeginCycles).data()); + fprintf(stderr, "%s=========================================================\n", prefix); + } +} diff --git a/util/datetime/cputimer.h b/util/datetime/cputimer.h new file mode 100644 index 00000000000..b24d6fae8e4 --- /dev/null +++ b/util/datetime/cputimer.h @@ -0,0 +1,128 @@ +#pragma once + +#include "base.h" + +#include +#include +#include + +class TTimer { +private: + TInstant Start_; + TStringStream Message_; + +public: + TTimer(const TStringBuf message = TStringBuf(" took: ")); + ~TTimer(); +}; + +class TSimpleTimer { + TInstant T; + +public: + TSimpleTimer() { + Reset(); + } + TDuration Get() const { + return TInstant::Now() - T; + } + void Reset() { + T = TInstant::Now(); + } +}; + +class TProfileTimer { + TDuration T; + +public: + TProfileTimer() { + Reset(); + } + TDuration Get() const { + return TRusage::Get().Utime - T; + } + TDuration Step() { + TRusage r; + r.Fill(); + TDuration d = r.Utime - T; + T = r.Utime; + return d; + } + void Reset() { + T = TRusage::Get().Utime; + } +}; + +/// Return cached processor cycle count per second. Method takes 1 second at first invocation. +/// Note, on older systems cycle rate may change during program lifetime, +/// so returned value may be incorrect. Modern Intel and AMD processors keep constant TSC rate. +ui64 GetCyclesPerMillisecond(); +void SetCyclesPerSecond(ui64 cycles); + +TDuration CyclesToDuration(ui64 cycles); +ui64 DurationToCycles(TDuration duration); + +// NBS-3400 - CyclesToDuration and DurationToCycles may overflow for long running events +TDuration CyclesToDurationSafe(ui64 cycles); +ui64 DurationToCyclesSafe(TDuration duration); + +class TPrecisionTimer { +private: + ui64 Start = 0; + +public: + TPrecisionTimer(); + + ui64 GetCycleCount() const; +}; + +TString FormatCycles(ui64 cycles); + +class TFormattedPrecisionTimer { +private: + ui64 Start; + const char* Message; + IOutputStream* Out; + +public: + TFormattedPrecisionTimer(const char* message = "took ", IOutputStream* out = &Cout); + ~TFormattedPrecisionTimer(); +}; + +class TFuncTimer { +public: + TFuncTimer(const char* func); + ~TFuncTimer(); + +private: + const TInstant Start_; + const char* Func_; +}; + +class TFakeTimer { +public: + inline TFakeTimer(const char* = nullptr) noexcept { + } +}; + +#if defined(WITH_DEBUG) + #define TDebugTimer TFuncTimer +#else + #define TDebugTimer TFakeTimer +#endif + +class TTimeLogger { +private: + TString Message; + bool Verbose; + bool OK; + time_t Begin; + ui64 BeginCycles; + +public: + TTimeLogger(const TString& message, bool verbose = true); + ~TTimeLogger(); + + void SetOK(); + double ElapsedTime() const; +}; diff --git a/util/datetime/cputimer_ut.cpp b/util/datetime/cputimer_ut.cpp new file mode 100644 index 00000000000..21a657d5c74 --- /dev/null +++ b/util/datetime/cputimer_ut.cpp @@ -0,0 +1,10 @@ +#include + +#include "cputimer.h" + +Y_UNIT_TEST_SUITE(TestCpuTimerSuite) { + Y_UNIT_TEST(TestCyclesToDurationSafe) { + ui64 cycles = DurationToCyclesSafe(TDuration::Hours(24)); + UNIT_ASSERT_VALUES_EQUAL(24, CyclesToDurationSafe(cycles).Hours()); + } +} diff --git a/util/datetime/parser.h b/util/datetime/parser.h new file mode 100644 index 00000000000..f0c1b4a0c78 --- /dev/null +++ b/util/datetime/parser.h @@ -0,0 +1,294 @@ +#pragma once + +// probably you do not need to include this file directly, use "util/datetime/base.h" + +#include "base.h" + +struct TDateTimeFields { + TDateTimeFields() { + Zero(*this); + ZoneOffsetMinutes = 0; + Hour = 0; + } + + ui32 Year; + ui32 Month; // 1..12 + ui32 Day; // 1 .. 31 + ui32 Hour; // 0 .. 23 + ui32 Minute; // 0 .. 59 + ui32 Second; // 0 .. 60 + ui32 MicroSecond; // 0 .. 999999 + i32 ZoneOffsetMinutes; + + void SetLooseYear(ui32 year) { + if (year < 60) + year += 100; + if (year < 160) + year += 1900; + Year = year; + } + + bool IsOk() const noexcept { + if (Year < 1970) + return false; + if (Month < 1 || Month > 12) + return false; + + unsigned int maxMonthDay = 31; + if (Month == 4 || Month == 6 || Month == 9 || Month == 11) { + maxMonthDay = 30; + } else if (Month == 2) { + if (Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)) + // leap year + maxMonthDay = 29; + else + maxMonthDay = 28; + } + if (Day > maxMonthDay) + return false; + + if (Hour > 23) + return false; + + if (Minute > 59) + return false; + + // handle leap second which is explicitly allowed by ISO 8601:2004(E) $2.2.2 + // https://datatracker.ietf.org/doc/html/rfc3339#section-5.6 + if (Second > 60) + return false; + + if (MicroSecond > 999999) + return false; + + if (Year == 1970 && Month == 1 && Day == 1) { + if ((i64)(3600 * Hour + 60 * Minute + Second) < (60 * ZoneOffsetMinutes)) + return false; + } + + return true; + } + + TInstant ToInstant(TInstant defaultValue) const { + time_t tt = ToTimeT(-1); + if (tt == -1) + return defaultValue; + return TInstant::Seconds(tt) + TDuration::MicroSeconds(MicroSecond); + } + + time_t ToTimeT(time_t defaultValue) const { + if (!IsOk()) + return defaultValue; + struct tm tm; + Zero(tm); + tm.tm_year = Year - 1900; + tm.tm_mon = Month - 1; + tm.tm_mday = Day; + tm.tm_hour = Hour; + tm.tm_min = Minute; + tm.tm_sec = Second; + time_t tt = TimeGM(&tm); + if (tt == -1) + return defaultValue; + return tt - ZoneOffsetMinutes * 60; + } +}; + +class TDateTimeParserBase { +public: + const TDateTimeFields& GetDateTimeFields() const { + return DateTimeFields; + } + +protected: + TDateTimeFields DateTimeFields; + int cs; //for ragel + int Sign; + int I; + int Dc; + +protected: + TDateTimeParserBase() + : DateTimeFields() + , cs(0) + , Sign(0) + , I(0xDEADBEEF) // to guarantee unittest break if ragel code is incorrect + , Dc(0xDEADBEEF) + { + } + + inline TInstant GetResult(int firstFinalState, TInstant defaultValue) const { + if (cs < firstFinalState) + return defaultValue; + return DateTimeFields.ToInstant(defaultValue); + } +}; + +#define DECLARE_PARSER(CLASS) \ + struct CLASS: public TDateTimeParserBase { \ + CLASS(); \ + bool ParsePart(const char* input, size_t len); \ + TInstant GetResult(TInstant defaultValue) const; \ + }; + +DECLARE_PARSER(TIso8601DateTimeParser) +DECLARE_PARSER(TRfc822DateTimeParser) +DECLARE_PARSER(THttpDateTimeParser) +DECLARE_PARSER(TX509ValidityDateTimeParser) +DECLARE_PARSER(TX509Validity4yDateTimeParser) + +#undef DECLARE_PARSER + +struct TDurationParser { + int cs; + + ui64 I; + ui32 Dc; + + i32 MultiplierPower; // 6 for seconds, 0 for microseconds, -3 for nanoseconds + i32 Multiplier; + ui64 IntegerPart; + ui32 FractionPart; + ui32 FractionDigits; + + TDurationParser(); + bool ParsePart(const char* input, size_t len); + TDuration GetResult(TDuration defaultValue) const; +}; + +/** +Depcrecated cause of default hour offset (+4 hours) +@see IGNIETFERRO-823 +*/ +struct TDateTimeFieldsDeprecated { + TDateTimeFieldsDeprecated() { + Zero(*this); + ZoneOffsetMinutes = (i32)TDuration::Hours(4).Minutes(); // legacy code + Hour = 11; + } + + ui32 Year; + ui32 Month; // 1..12 + ui32 Day; // 1 .. 31 + ui32 Hour; // 0 .. 23 + ui32 Minute; // 0 .. 59 + ui32 Second; // 0 .. 60 + ui32 MicroSecond; // 0 .. 999999 + i32 ZoneOffsetMinutes; + + void SetLooseYear(ui32 year) { + if (year < 60) + year += 100; + if (year < 160) + year += 1900; + Year = year; + } + + bool IsOk() const noexcept { + if (Year < 1970) + return false; + if (Month < 1 || Month > 12) + return false; + + unsigned int maxMonthDay = 31; + if (Month == 4 || Month == 6 || Month == 9 || Month == 11) { + maxMonthDay = 30; + } else if (Month == 2) { + if (Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)) + // leap year + maxMonthDay = 29; + else + maxMonthDay = 28; + } + if (Day > maxMonthDay) + return false; + + if (Hour > 23) + return false; + + if (Minute > 59) + return false; + + if (Second > 60) + return false; + + if (MicroSecond > 999999) + return false; + + if (Year == 1970 && Month == 1 && Day == 1) { + if ((i64)(3600 * Hour + 60 * Minute + Second) < (60 * ZoneOffsetMinutes)) + return false; + } + + return true; + } + + TInstant ToInstant(TInstant defaultValue) const { + time_t tt = ToTimeT(-1); + if (tt == -1) + return defaultValue; + return TInstant::Seconds(tt) + TDuration::MicroSeconds(MicroSecond); + } + + time_t ToTimeT(time_t defaultValue) const { + if (!IsOk()) + return defaultValue; + struct tm tm; + Zero(tm); + tm.tm_year = Year - 1900; + tm.tm_mon = Month - 1; + tm.tm_mday = Day; + tm.tm_hour = Hour; + tm.tm_min = Minute; + tm.tm_sec = Second; + time_t tt = TimeGM(&tm); + if (tt == -1) + return defaultValue; + return tt - ZoneOffsetMinutes * 60; + } +}; + +class TDateTimeParserBaseDeprecated { +public: + const TDateTimeFieldsDeprecated& GetDateTimeFields() const { + return DateTimeFields; + } + +protected: + TDateTimeFieldsDeprecated DateTimeFields; + int cs; //for ragel + int Sign; + int I; + int Dc; + +protected: + TDateTimeParserBaseDeprecated() + : DateTimeFields() + , cs(0) + , Sign(0) + , I(0xDEADBEEF) // to guarantee unittest break if ragel code is incorrect + , Dc(0xDEADBEEF) + { + } + + inline TInstant GetResult(int firstFinalState, TInstant defaultValue) const { + if (cs < firstFinalState) + return defaultValue; + return DateTimeFields.ToInstant(defaultValue); + } +}; + +#define DECLARE_PARSER(CLASS) \ + struct CLASS: public TDateTimeParserBaseDeprecated { \ + CLASS(); \ + bool ParsePart(const char* input, size_t len); \ + TInstant GetResult(TInstant defaultValue) const; \ + }; + +DECLARE_PARSER(TIso8601DateTimeParserDeprecated) +DECLARE_PARSER(TRfc822DateTimeParserDeprecated) +DECLARE_PARSER(THttpDateTimeParserDeprecated) +DECLARE_PARSER(TX509ValidityDateTimeParserDeprecated) +DECLARE_PARSER(TX509Validity4yDateTimeParserDeprecated) + +#undef DECLARE_PARSER diff --git a/util/datetime/parser.rl6 b/util/datetime/parser.rl6 new file mode 100644 index 00000000000..5ea0baedc74 --- /dev/null +++ b/util/datetime/parser.rl6 @@ -0,0 +1,802 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + + +%%{ + +machine DateTimeParserCommon; + +sp = ' '; + +action clear_int { + I = 0; + Dc = 0; +} + +action update_int { + I = I * 10 + (fc - '0'); + ++Dc; +} + +int = (digit+) + >clear_int + $update_int; + +int1 = digit + >clear_int + $update_int; + +int2 = (digit digit) + >clear_int + $update_int; + +int3 = (digit digit digit) + >clear_int + $update_int; + +int4 = (digit digit digit digit) + >clear_int + $update_int; + +int12 = (digit digit?) + >clear_int + $update_int; + +int24 = ( digit digit ( digit digit )? ) + >clear_int + $update_int; + +# According to both RFC2822 and RFC2616 dates MUST be case-sensitive, +# but Andrey fomichev@ wants relaxed parser + +month3 = + 'Jan'i %{ DateTimeFields.Month = 1; } + | 'Feb'i %{ DateTimeFields.Month = 2; } + | 'Mar'i %{ DateTimeFields.Month = 3; } + | 'Apr'i %{ DateTimeFields.Month = 4; } + | 'May'i %{ DateTimeFields.Month = 5; } + | 'Jun'i %{ DateTimeFields.Month = 6; } + | 'Jul'i %{ DateTimeFields.Month = 7; } + | 'Aug'i %{ DateTimeFields.Month = 8; } + | 'Sep'i %{ DateTimeFields.Month = 9; } + | 'Oct'i %{ DateTimeFields.Month = 10; } + | 'Nov'i %{ DateTimeFields.Month = 11; } + | 'Dec'i %{ DateTimeFields.Month = 12; }; + +wkday = 'Mon'i | 'Tue'i | 'Wed'i | 'Thu'i | 'Fri'i | 'Sat'i | 'Sun'i; +weekday = 'Monday'i | 'Tuesday'i | 'Wednesday'i | 'Thursday'i + | 'Friday'i | 'Saturday'i | 'Sunday'i; + +action set_second { DateTimeFields.Second = I; } +action set_minute { DateTimeFields.Minute = I; } +action set_hour { DateTimeFields.Hour = I; } +action set_day { DateTimeFields.Day = I; } +action set_month { DateTimeFields.Month = I; } +action set_year { DateTimeFields.SetLooseYear(I); } +action set_precise_year { DateTimeFields.Year = I; } +action set_zone_utc { DateTimeFields.ZoneOffsetMinutes = 0; } + +}%% + +%%{ + +machine RFC822DateParser; + +################# RFC 2822 3.3 Full Date ################### + +include DateTimeParserCommon; + +ws1 = (space+); +ws0 = (space*); +dow_spec = ( wkday ',' )?; + +day = int12 %set_day; +year = int24 %set_year; + +# actually it must be from 0 to 23 +hour = int2 %set_hour; + +# actually it must be from 0 to 59 +min = int2 %set_minute; + +# actually it must be from 0 to 59 +sec = int2 %set_second; + +sec_spec = ( ':' . sec )?; + +# so called "military zone offset". I hardly believe someone uses it now, but we MUST respect RFc822 +action set_mil_offset { + char c = (char)toupper(fc); + if (c == 'Z') + DateTimeFields.ZoneOffsetMinutes = 0; + else { + if (c <= 'M') { + // ['A'..'M'] \ 'J' + if (c < 'J') + DateTimeFields.ZoneOffsetMinutes = (i32)TDuration::Hours(c - 'A' + 1).Minutes(); + else + DateTimeFields.ZoneOffsetMinutes = (i32)TDuration::Hours(c - 'A').Minutes(); + } else { + // ['N'..'Y'] + DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(c - 'N' + 1).Minutes(); + } + } +} + +action set_digit_offset { + DateTimeFields.ZoneOffsetMinutes = Sign * (i32)(TDuration::Hours(I / 100) + TDuration::Minutes(I % 100)).Minutes(); +} + +mil_zone = /[A-IK-Za-ik-z]/ $set_mil_offset; + +# actions % were replaced with @ (when the script was migrated to ragel 5.24) +# because ragel 5.24 does not call to the % action if it be called at the very end of string. +# it is a bug in ragel 5 because ragel 6.2 works correctly with % at the end of string. +# see http://www.complang.org/ragel/ChangeLog. + +zone = 'UT' @{ DateTimeFields.ZoneOffsetMinutes = 0; } + | 'GMT' @{ DateTimeFields.ZoneOffsetMinutes = 0; } + | 'EST' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(5).Minutes();} + | 'EDT' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(4).Minutes(); } + | 'CST' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(6).Minutes();} + | 'CDT' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(5).Minutes(); } + | 'MST' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(7).Minutes();} + | 'MDT' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(6).Minutes(); } + | 'PST' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(8).Minutes();} + | 'PDT' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(7).Minutes(); }; + +digit_offset = ('+' | '-') > { Sign = fc == '+' ? 1 : -1; } . int4 @set_digit_offset; + +offset = ( zone | mil_zone | digit_offset ); + +rfc822datetime = ws0 . dow_spec . ws0 . day . ws1 . month3 . ws1 . year . ws1 . hour . ':' . min . sec_spec . ws1 . offset . ws0; + +main := rfc822datetime; + +write data noerror; + +}%% + +TRfc822DateTimeParserDeprecated::TRfc822DateTimeParserDeprecated() { + %% write init; +} + +bool TRfc822DateTimeParserDeprecated::ParsePart(const char* input, size_t len) { + const char* p = input; + const char* pe = input + len; + + %% write exec; + return cs != %%{ write error; }%%; +} + +TRfc822DateTimeParser::TRfc822DateTimeParser() { + %% write init; +} + +bool TRfc822DateTimeParser::ParsePart(const char* input, size_t len) { + const char* p = input; + const char* pe = input + len; + + %% write exec; + return cs != %%{ write error; }%%; +} + +%%{ + +machine ISO8601DateTimeParser; + +include DateTimeParserCommon; + +year = int4 @set_precise_year; +month = int2 @set_month; +day = int2 @set_day; +hour = int2 @set_hour; +minute = int2 @set_minute; +second = int2 @set_second; +secondFrac = digit {1,6} >clear_int $update_int @{ + ui32 us = I; + for (int k = Dc; k < 6; ++k) { + us *= 10; + } + DateTimeFields.MicroSecond = us; +}; +secondFracTail = (digit*); + +zoneZ = [Zz] @set_zone_utc; +zoneOffset = space? . ('+' | '-') >{ Sign = fc == '+' ? 1 : -1; } . int2 @{ DateTimeFields.ZoneOffsetMinutes = Sign * (i32)TDuration::Hours(I).Minutes(); } . (':')? . (int2 @{ DateTimeFields.ZoneOffsetMinutes += I * Sign; })?; +zone = zoneZ | zoneOffset; + +iso8601date = (year . '-' . month . '-' . day) | (year . month . day); +iso8601time = (hour . ':' . minute . (':' . second ('.' secondFrac secondFracTail)?)?) | (hour . minute . second?); + +iso8601datetime = iso8601date . ([Tt ] . iso8601time . zone?)?; + +main := iso8601datetime; + +write data noerror; + +}%% + +TIso8601DateTimeParserDeprecated::TIso8601DateTimeParserDeprecated() { + %% write init; +} + +bool TIso8601DateTimeParserDeprecated::ParsePart(const char* input, size_t len) { + const char* p = input; + const char* pe = input + len; + + %% write exec; + return cs != %%{ write error; }%%; +} + +TIso8601DateTimeParser::TIso8601DateTimeParser() { + %% write init; +} + +bool TIso8601DateTimeParser::ParsePart(const char* input, size_t len) { + const char* p = input; + const char* pe = input + len; + + %% write exec; + return cs != %%{ write error; }%%; +} + +%%{ + +machine HttpDateTimeParser; + +include DateTimeParserCommon; + +################# RFC 2616 3.3.1 Full Date ################# + +time = int2 %set_hour ':' int2 %set_minute ':' int2 %set_second; +date1 = int2 %set_day ' ' month3 ' ' int4 %set_year; +date2 = int2 %set_day '-' month3 '-' int2 %set_year; +date3 = month3 sp (int2 | sp int1) %set_day; + +rfc1123_date = wkday ',' sp date1 sp time sp 'GMT'i; +rfc850_date = weekday ',' sp date2 sp time sp 'GMT'i; +asctime_date = wkday sp date3 sp time sp int4 @set_year; +http_date = (rfc1123_date | rfc850_date | asctime_date) @set_zone_utc; + +}%% + +%%{ + +machine HttpDateTimeParserStandalone; + +include HttpDateTimeParser; + +main := http_date; + +write data noerror; + +}%% + +THttpDateTimeParserDeprecated::THttpDateTimeParserDeprecated() { + %% write init; +} + +bool THttpDateTimeParserDeprecated::ParsePart(const char* input, size_t len) { + const char* p = input; + const char* pe = input + len; + + %% write exec; + return cs != %%{ write error; }%%; +} + +THttpDateTimeParser::THttpDateTimeParser() { + %% write init; +} + +bool THttpDateTimeParser::ParsePart(const char* input, size_t len) { + const char* p = input; + const char* pe = input + len; + + %% write exec; + return cs != %%{ write error; }%%; +} + +%%{ + +machine X509ValidityDateTimeParser; + +include DateTimeParserCommon; + +################# X.509 certificate validity time (see rfc5280 4.1.2.5.*) ################# + +year = int2 @{ DateTimeFields.Year = (I < 50 ? I + 2000 : I + 1900); }; +month = int2 @set_month; +day = int2 @set_day; +hour = int2 @set_hour; +minute = int2 @set_minute; +second = int2 @set_second; +zone = 'Z' @set_zone_utc; + +main := year . month . day . hour . minute . second . zone; + +write data noerror; + +}%% + +TX509ValidityDateTimeParserDeprecated::TX509ValidityDateTimeParserDeprecated() { + %% write init; +} + +bool TX509ValidityDateTimeParserDeprecated::ParsePart(const char *input, size_t len) { + const char *p = input; + const char *pe = input + len; + + %% write exec; + return cs != %%{ write error; }%%; +} + +TX509ValidityDateTimeParser::TX509ValidityDateTimeParser() { + %% write init; +} + +bool TX509ValidityDateTimeParser::ParsePart(const char *input, size_t len) { + const char *p = input; + const char *pe = input + len; + + %% write exec; + return cs != %%{ write error; }%%; +} + +%%{ + +machine X509Validity4yDateTimeParser; + +include DateTimeParserCommon; + +year = int4 @{ DateTimeFields.Year = I; }; +month = int2 @set_month; +day = int2 @set_day; +hour = int2 @set_hour; +minute = int2 @set_minute; +second = int2 @set_second; +zone = 'Z' @set_zone_utc; + + +main := year . month . day . hour . minute . second . zone; + +write data noerror; + +}%% + +TX509Validity4yDateTimeParserDeprecated::TX509Validity4yDateTimeParserDeprecated() { + %% write init; +} + +bool TX509Validity4yDateTimeParserDeprecated::ParsePart(const char *input, size_t len) { + const char *p = input; + const char *pe = input + len; + + %% write exec; + return cs != %%{ write error; }%%; +} + +TX509Validity4yDateTimeParser::TX509Validity4yDateTimeParser() { + %% write init; +} + +bool TX509Validity4yDateTimeParser::ParsePart(const char *input, size_t len) { + const char *p = input; + const char *pe = input + len; + + %% write exec; + return cs != %%{ write error; }%%; +} + +TInstant TIso8601DateTimeParserDeprecated::GetResult(TInstant defaultValue) const { + Y_UNUSED(ISO8601DateTimeParser_en_main); + return TDateTimeParserBaseDeprecated::GetResult(ISO8601DateTimeParser_first_final, defaultValue); +} + +TInstant TRfc822DateTimeParserDeprecated::GetResult(TInstant defaultValue) const { + Y_UNUSED(RFC822DateParser_en_main); + return TDateTimeParserBaseDeprecated::GetResult(RFC822DateParser_first_final, defaultValue); +} + +TInstant THttpDateTimeParserDeprecated::GetResult(TInstant defaultValue) const { + Y_UNUSED(HttpDateTimeParserStandalone_en_main); + return TDateTimeParserBaseDeprecated::GetResult(HttpDateTimeParserStandalone_first_final, defaultValue); +} + +TInstant TX509ValidityDateTimeParserDeprecated::GetResult(TInstant defaultValue) const { + Y_UNUSED(X509ValidityDateTimeParser_en_main); + return TDateTimeParserBaseDeprecated::GetResult(X509ValidityDateTimeParser_first_final, defaultValue); +} + +TInstant TX509Validity4yDateTimeParserDeprecated::GetResult(TInstant defaultValue) const { + Y_UNUSED(X509Validity4yDateTimeParser_en_main); + return TDateTimeParserBaseDeprecated::GetResult(X509Validity4yDateTimeParser_first_final, defaultValue); +} + +TInstant TIso8601DateTimeParser::GetResult(TInstant defaultValue) const { + Y_UNUSED(ISO8601DateTimeParser_en_main); + return TDateTimeParserBase::GetResult(ISO8601DateTimeParser_first_final, defaultValue); +} + +TInstant TRfc822DateTimeParser::GetResult(TInstant defaultValue) const { + Y_UNUSED(RFC822DateParser_en_main); + return TDateTimeParserBase::GetResult(RFC822DateParser_first_final, defaultValue); +} + +TInstant THttpDateTimeParser::GetResult(TInstant defaultValue) const { + Y_UNUSED(HttpDateTimeParserStandalone_en_main); + return TDateTimeParserBase::GetResult(HttpDateTimeParserStandalone_first_final, defaultValue); +} + +TInstant TX509ValidityDateTimeParser::GetResult(TInstant defaultValue) const { + Y_UNUSED(X509ValidityDateTimeParser_en_main); + return TDateTimeParserBase::GetResult(X509ValidityDateTimeParser_first_final, defaultValue); +} + +TInstant TX509Validity4yDateTimeParser::GetResult(TInstant defaultValue) const { + Y_UNUSED(X509Validity4yDateTimeParser_en_main); + return TDateTimeParserBase::GetResult(X509Validity4yDateTimeParser_first_final, defaultValue); +} + +template +static inline TResult Parse(const char* input, size_t len, TResult defaultValue) { + TParser parser; + if (!parser.ParsePart(input, len)) + return defaultValue; + return parser.GetResult(defaultValue); +} + +template +static inline TResult ParseUnsafe(const char* input, size_t len) { + TResult r = Parse(input, len, TResult::Max()); + if (ThrowExceptionOnFailure && r == TResult::Max()) + ythrow TDateTimeParseException() << "error in datetime parsing. Input data: " << TStringBuf(input, len); + return r; +} + +TInstant TInstant::ParseIso8601Deprecated(const TStringBuf input) { + return ParseUnsafe(input.data(), input.size()); +} + +TInstant TInstant::ParseRfc822Deprecated(const TStringBuf input) { + return ParseUnsafe(input.data(), input.size()); +} + +TInstant TInstant::ParseHttpDeprecated(const TStringBuf input) { + return ParseUnsafe(input.data(), input.size()); +} + +TInstant TInstant::ParseX509ValidityDeprecated(const TStringBuf input) { + switch (input.size()) { + case 13: + return ParseUnsafe(input.data(), 13); + case 15: + return ParseUnsafe(input.data(), 15); + default: + ythrow TDateTimeParseException(); + } +} + +bool TInstant::TryParseIso8601Deprecated(const TStringBuf input, TInstant& instant) { + const auto parsed = ParseUnsafe(input.data(), input.size()); + if (TInstant::Max() == parsed) { + return false; + } + instant = parsed; + return true; +} + +bool TInstant::TryParseRfc822Deprecated(const TStringBuf input, TInstant& instant) { + const auto parsed = ParseUnsafe(input.data(), input.size()); + if (TInstant::Max() == parsed) { + return false; + } + instant = parsed; + return true; +} + +bool TInstant::TryParseHttpDeprecated(const TStringBuf input, TInstant& instant) { + const auto parsed = ParseUnsafe(input.data(), input.size()); + if (TInstant::Max() == parsed) { + return false; + } + instant = parsed; + return true; +} + +bool TInstant::TryParseX509Deprecated(const TStringBuf input, TInstant& instant) { + TInstant parsed; + switch (input.size()) { + case 13: + parsed = ParseUnsafe(input.data(), 13); + break; + case 15: + parsed = ParseUnsafe(input.data(), 15); + break; + default: + return false; + } + if (TInstant::Max() == parsed) { + return false; + } + instant = parsed; + return true; +} + +TInstant TInstant::ParseIso8601(const TStringBuf input) { + return ParseUnsafe(input.data(), input.size()); +} + +TInstant TInstant::ParseRfc822(const TStringBuf input) { + return ParseUnsafe(input.data(), input.size()); +} + +TInstant TInstant::ParseHttp(const TStringBuf input) { + return ParseUnsafe(input.data(), input.size()); +} + +TInstant TInstant::ParseX509Validity(const TStringBuf input) { + switch (input.size()) { + case 13: + return ParseUnsafe(input.data(), 13); + case 15: + return ParseUnsafe(input.data(), 15); + default: + ythrow TDateTimeParseException(); + } +} + +bool TInstant::TryParseIso8601(const TStringBuf input, TInstant& instant) { + const auto parsed = ParseUnsafe(input.data(), input.size()); + if (TInstant::Max() == parsed) { + return false; + } + instant = parsed; + return true; +} + +bool TInstant::TryParseRfc822(const TStringBuf input, TInstant& instant) { + const auto parsed = ParseUnsafe(input.data(), input.size()); + if (TInstant::Max() == parsed) { + return false; + } + instant = parsed; + return true; +} + +bool TInstant::TryParseHttp(const TStringBuf input, TInstant& instant) { + const auto parsed = ParseUnsafe(input.data(), input.size()); + if (TInstant::Max() == parsed) { + return false; + } + instant = parsed; + return true; +} + +bool TInstant::TryParseX509(const TStringBuf input, TInstant& instant) { + TInstant parsed; + switch (input.size()) { + case 13: + parsed = ParseUnsafe(input.data(), 13); + break; + case 15: + parsed = ParseUnsafe(input.data(), 15); + break; + default: + return false; + } + if (TInstant::Max() == parsed) { + return false; + } + instant = parsed; + return true; +} + +bool ParseRFC822DateTimeDeprecated(const char* input, time_t& utcTime) { + return ParseRFC822DateTimeDeprecated(input, strlen(input), utcTime); +} + +bool ParseISO8601DateTimeDeprecated(const char* input, time_t& utcTime) { + return ParseISO8601DateTimeDeprecated(input, strlen(input), utcTime); +} + +bool ParseHTTPDateTimeDeprecated(const char* input, time_t& utcTime) { + return ParseHTTPDateTimeDeprecated(input, strlen(input), utcTime); +} + +bool ParseX509ValidityDateTimeDeprecated(const char* input, time_t& utcTime) { + return ParseX509ValidityDateTimeDeprecated(input, strlen(input), utcTime); +} + +bool ParseRFC822DateTimeDeprecated(const char* input, size_t inputLen, time_t& utcTime) { + try { + utcTime = ParseUnsafe(input, inputLen).TimeT(); + return true; + } catch (const TDateTimeParseException&) { + return false; + } +} + +bool ParseISO8601DateTimeDeprecated(const char* input, size_t inputLen, time_t& utcTime) { + try { + utcTime = ParseUnsafe(input, inputLen).TimeT(); + return true; + } catch (const TDateTimeParseException&) { + return false; + } +} + +bool ParseHTTPDateTimeDeprecated(const char* input, size_t inputLen, time_t& utcTime) { + try { + utcTime = ParseUnsafe(input, inputLen).TimeT(); + return true; + } catch (const TDateTimeParseException&) { + return false; + } +} + +bool ParseX509ValidityDateTimeDeprecated(const char* input, size_t inputLen, time_t& utcTime) { + TInstant r; + switch (inputLen) { + case 13: + r = Parse(input, 13, TInstant::Max()); + break; + case 15: + r = Parse(input, 15, TInstant::Max()); + break; + default: + return false; + } + if (r == TInstant::Max()) + return false; + utcTime = r.TimeT(); + return true; +} + +bool ParseRFC822DateTime(const char* input, time_t& utcTime) { + return ParseRFC822DateTime(input, strlen(input), utcTime); +} + +bool ParseISO8601DateTime(const char* input, time_t& utcTime) { + return ParseISO8601DateTime(input, strlen(input), utcTime); +} + +bool ParseHTTPDateTime(const char* input, time_t& utcTime) { + return ParseHTTPDateTime(input, strlen(input), utcTime); +} + +bool ParseX509ValidityDateTime(const char* input, time_t& utcTime) { + return ParseX509ValidityDateTime(input, strlen(input), utcTime); +} + +bool ParseRFC822DateTime(const char* input, size_t inputLen, time_t& utcTime) { + try { + utcTime = ParseUnsafe(input, inputLen).TimeT(); + return true; + } catch (const TDateTimeParseException&) { + return false; + } +} + +bool ParseISO8601DateTime(const char* input, size_t inputLen, time_t& utcTime) { + try { + utcTime = ParseUnsafe(input, inputLen).TimeT(); + return true; + } catch (const TDateTimeParseException&) { + return false; + } +} + +bool ParseHTTPDateTime(const char* input, size_t inputLen, time_t& utcTime) { + try { + utcTime = ParseUnsafe(input, inputLen).TimeT(); + return true; + } catch (const TDateTimeParseException&) { + return false; + } +} + +bool ParseX509ValidityDateTime(const char* input, size_t inputLen, time_t& utcTime) { + TInstant r; + switch (inputLen) { + case 13: + r = Parse(input, 13, TInstant::Max()); + break; + case 15: + r = Parse(input, 15, TInstant::Max()); + break; + default: + return false; + } + if (r == TInstant::Max()) + return false; + utcTime = r.TimeT(); + return true; +} + +%%{ + +machine TDurationParser; + +include DateTimeParserCommon; + + +multiplier + = '' # >{ MultiplierPower = 6; } # work around Ragel bugs + | 'w' @{ MultiplierPower = 6; Multiplier = 604800; } + | 'd' @{ MultiplierPower = 6; Multiplier = 86400; } + | 'h' @{ MultiplierPower = 6; Multiplier = 3600; } + | 'm' @{ MultiplierPower = 6; Multiplier = 60; } + | 's' @{ MultiplierPower = 6; Multiplier = 1; } + | 'ms' @{ MultiplierPower = 3; Multiplier = 1; } + | 'us' @{ MultiplierPower = 0; Multiplier = 1; } + | 'ns' @{ MultiplierPower = -3; Multiplier = 1; } + ; + +integer = int @{ IntegerPart = I; }; + +fraction = '.' digit {1,6} >clear_int $update_int @{ FractionPart = I; FractionDigits = Dc; } digit*; + +duration = integer fraction? multiplier; + +main := duration; + +write data noerror; + +}%% + +TDurationParser::TDurationParser() + : cs(0) + , I(0) + , Dc(0) + , MultiplierPower(6) + , Multiplier(1) + , IntegerPart(0) + , FractionPart(0) + , FractionDigits(0) +{ + Y_UNUSED(TDurationParser_en_main); + %% write init; +} + +bool TDurationParser::ParsePart(const char* input, size_t len) { + const char* p = input; + const char* pe = input + len; + + %% write exec; + return cs != %%{ write error; }%%; +} + +static inline ui64 DecPower(ui64 part, i32 power) { + if (power >= 0) + return part * Power(10, power); + return part / Power(10, -power); +} + +TDuration TDurationParser::GetResult(TDuration defaultValue) const { + if (cs < TDurationParser_first_final) + return defaultValue; + ui64 us = 0; + us += Multiplier * DecPower(IntegerPart, MultiplierPower); + us += Multiplier * DecPower(FractionPart, MultiplierPower - FractionDigits); + return TDuration::MicroSeconds(us); +} + +bool TDuration::TryParse(const TStringBuf input, TDuration& result) { + TDuration r = ::Parse(input.data(), input.size(), TDuration::Max()); + if (r == TDuration::Max()) + return false; + result = r; + return true; +} + +TDuration TDuration::Parse(const TStringBuf input) { + return ParseUnsafe(input.data(), input.size()); +} diff --git a/util/datetime/parser_deprecated_ut.cpp b/util/datetime/parser_deprecated_ut.cpp new file mode 100644 index 00000000000..6ad9f885b1c --- /dev/null +++ b/util/datetime/parser_deprecated_ut.cpp @@ -0,0 +1,576 @@ +#include "parser.h" + +#include + +static const time_t SECONDS_PER_HOUR = 3600; +static const time_t SECONDS_PER_MINUTE = 60; + +Y_UNIT_TEST_SUITE(TDateTimeParseTestDeprecated) { + Y_UNIT_TEST(TestRfc822CorrectDeprecated) { + bool r = false; + time_t t = 0; + + // predefined time zones + + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 UT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 GMT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 EST", t); + UNIT_ASSERT(r); + UNIT_ASSERT_VALUES_EQUAL(t, (time_t)1109964885 + 5 * SECONDS_PER_HOUR); + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 EDT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 4 * SECONDS_PER_HOUR); + + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 CST", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 6 * SECONDS_PER_HOUR); + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 CDT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 5 * SECONDS_PER_HOUR); + + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 MST", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 7 * SECONDS_PER_HOUR); + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 MDT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 6 * SECONDS_PER_HOUR); + + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 PST", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 8 * SECONDS_PER_HOUR); + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 PDT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 7 * SECONDS_PER_HOUR); + + // optinal century + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 05 19:34:45 UT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + // + optional day of week + r = ParseRFC822DateTimeDeprecated("4 Mar 05 19:34:45 UT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + // + optional seconds + r = ParseRFC822DateTimeDeprecated("4 Mar 05 19:34 UT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964840); + + // local differential hour+min + + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 +0300", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109954085); + + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 05 19:34:45 +0300", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109954085); + + r = ParseRFC822DateTimeDeprecated("21 Apr 1999 23:40:00 +0400", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)924723600); + + r = ParseRFC822DateTimeDeprecated("21 Apr 99 23:40 +0400", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)924723600); + + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 -0300", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 3 * SECONDS_PER_HOUR); + + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34 -0300", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964840 + 3 * SECONDS_PER_HOUR); + + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 05 19:34:45 -0330", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 3 * SECONDS_PER_HOUR + 30 * SECONDS_PER_MINUTE); + + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 05 19:34 -0330", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964840 + 3 * SECONDS_PER_HOUR + 30 * SECONDS_PER_MINUTE); + + r = ParseRFC822DateTimeDeprecated("4 Mar 2005 19:34:45 -1030", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 10 * SECONDS_PER_HOUR + 30 * SECONDS_PER_MINUTE); + + r = ParseRFC822DateTimeDeprecated("4 Mar 2005 19:34 -1030", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964840 + 10 * SECONDS_PER_HOUR + 30 * SECONDS_PER_MINUTE); + + // white spaces + + r = ParseRFC822DateTimeDeprecated("Fri,4 Mar 2005 19:34:45 UT", t); // no space after comma + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + r = ParseRFC822DateTimeDeprecated(" Fri, 4 Mar 2005 19:34:45 UT ", t); // several spaces, leading and trailing + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + r = ParseRFC822DateTimeDeprecated(" \t Fri, \t 4 \t Mar \t 2005 \t 19:34:45 \t UT \t ", t); // spaces with tabs + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + r = ParseRFC822DateTimeDeprecated("Thu, 01 Jan 1970 03:00:00 +0300", t); // spaces with tabs + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)0); + + r = ParseRFC822DateTimeDeprecated("Sat, 14 Feb 2009 02:31:30 +0300", t); // spaces with tabs + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1234567890); + } + + time_t GetOffset(char militaryZone) { + char ch = (char)toupper(militaryZone); + if (ch == 'Z') { + return 0; + } else if (ch >= 'A' && ch < 'J') { + return (ch - 'A' + 1) * SECONDS_PER_HOUR; + } else if (ch > 'J' && ch <= 'M') { + return (ch - 'A') * SECONDS_PER_HOUR; + } else if (ch >= 'N' && ch <= 'Y') { + return -(ch - 'N' + 1) * SECONDS_PER_HOUR; + } else { + ythrow yexception() << "Invalid military zone."; + } + } + + void DoTestMilitaryZones(char firstChar, char lastChar) { + const time_t utcTime = 1109964885; // Fri, 4 Mar 2005 19:34:45 UT + char text[] = "Fri, 4 Mar 2005 19:34:45 A"; + const size_t zoneCharIndex = strlen(text) - 1; + + for (char militaryZone = firstChar; militaryZone <= lastChar; ++militaryZone) { + time_t t = 0; + const time_t offset = GetOffset(militaryZone); + // the last character is replaced with next zone symbol + text[zoneCharIndex] = militaryZone; + + UNIT_ASSERT(ParseRFC822DateTimeDeprecated(text, t)); + UNIT_ASSERT_EQUAL(t, utcTime - offset); + } + } + + Y_UNIT_TEST(TestRfc822MilitaryZonesDeprecated) { + DoTestMilitaryZones('A', 'I'); + DoTestMilitaryZones('K', 'Z'); + DoTestMilitaryZones('a', 'i'); + DoTestMilitaryZones('k', 'z'); + } + + Y_UNIT_TEST(TestRfc822IncorrectDatesDeprecated) { + bool r = true; + time_t t = 0; + + t = 12345; + r = ParseRFC822DateTimeDeprecated("", t); + UNIT_ASSERT(!r); + UNIT_ASSERT_EQUAL(t, (time_t)12345); + + t = 223344; + r = ParseRFC822DateTimeDeprecated("Fri, some junk", t); + UNIT_ASSERT(!r); + UNIT_ASSERT_EQUAL(t, (time_t)223344); + + t = 54321; + r = ParseRFC822DateTimeDeprecated("Fri, 4 Mar 2005 19:34:45 UTC", t); + UNIT_ASSERT(!r); + UNIT_ASSERT_EQUAL(t, (time_t)54321); + + // TODO: check semantic validity of parsed date (30 Feb, 88:90 etc.). + // The following tests MUST fail (they don't now) + // r = ParseRFC822DateTimeDeprecated("45 Mar 2005 19:34:45 UT", t); + // UNIT_ASSERT_EQUAL(r, false); + + // r = ParseRFC822DateTimeDeprecated("29 Feb 2005 19:34:45 +0300", t); + // UNIT_ASSERT_EQUAL(r, false); + + // r = ParseRFC822DateTimeDeprecated("31 Apr 2004 19:34:45 +0300", t); + // UNIT_ASSERT_EQUAL(r, false); + + r = ParseRFC822DateTimeDeprecated("17 Nov 2008 19:34:45", t); // no specified time zone + UNIT_ASSERT(!r); + + r = ParseRFC822DateTimeDeprecated("17 Nov 200 19:34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("17 Nov 8 19:34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("17 Nov 20008 19:34:45 UT", t); + UNIT_ASSERT(!r); + + r = ParseRFC822DateTimeDeprecated("17 Nov 2008 1:34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("17 Nov 2008 123:34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("17 Nov 2008 19:1:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("17 Nov 2008 19:123:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("17 Nov 2008 19:34:1 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("17 Nov 2008 19:34:123 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("17 Nov 2008 19:34:12.12 UT", t); // fractions of second are now allowed + UNIT_ASSERT(!r); + + r = ParseRFC822DateTimeDeprecated("Mon , 17 Nov 2005 19:34:45 UT", t); // space after day before the comma + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("Mon, 17 Nov 2005 19 :34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("Mon, 17 Nov 2005 19: 34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("Mon, 17 Nov 2005 19:34 :45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("Mon, 17 Nov 2005 19:34: 45 UT", t); + UNIT_ASSERT(!r); + + r = ParseRFC822DateTimeDeprecated("Monday, 17 Nov 2005 19:34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("Mon, 17 November 2008 19:34:45 UT", t); + UNIT_ASSERT(!r); + + r = ParseRFC822DateTimeDeprecated("Mon, 17 Nov 2008 19:34:45 +3", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("Mon, 17 Nov 2008 19:34:45 +03", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("Mon, 17 Nov 2008 19:34:45 +030", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("Mon, 17 Nov 2008 19:34:45 +03030", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("Mon, 17 Nov 2008 19:34:45 -3", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("Mon, 17 Nov 2008 19:34:45 -03", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("Mon, 17 Nov 2008 19:34:45 -030", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTimeDeprecated("Mon, 17 Nov 2008 19:34:45 -03030", t); + UNIT_ASSERT(!r); + } + + Y_UNIT_TEST(TestRfc822PartialDeprecated) { + TRfc822DateTimeParserDeprecated p; + const char* part1 = "Fri, 4 Mar 05 1"; + const char* part2 = "9:34:45 +0300"; + UNIT_ASSERT(p.ParsePart(part1, strlen(part1))); + UNIT_ASSERT(p.ParsePart(part2, strlen(part2))); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(1109954085), p.GetResult(TInstant::Max())); + p = TRfc822DateTimeParserDeprecated(); + const char* part3 = "Fri, 4 Mar 05 19:34:46 +0300"; + UNIT_ASSERT(p.ParsePart(part3, strlen(part3))); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(1109954086), p.GetResult(TInstant::Zero())); + } + + Y_UNIT_TEST(TestIso8601PartialDeprecated) { + TIso8601DateTimeParserDeprecated p; + const char* part1 = "1990-03-15T15:1"; + const char* part2 = "6:17+0732"; + UNIT_ASSERT(p.ParsePart(part1, strlen(part1))); + UNIT_ASSERT(p.ParsePart(part2, strlen(part2))); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(637487057), p.GetResult(TInstant::Max())); + p = TIso8601DateTimeParserDeprecated(); + const char* part3 = "1990-03-15T15:16:18+0732"; + UNIT_ASSERT(p.ParsePart(part3, strlen(part3))); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(637487058), p.GetResult(TInstant::Zero())); + } + + Y_UNIT_TEST(TestIso8601CorrectDeprecatedDeprecated) { + bool ret; + time_t t; + + // ISO 8601 actually does not allow time without time zone + ret = ParseISO8601DateTimeDeprecated("1990-03-15", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637484400); + + // some normal dates + ret = ParseISO8601DateTimeDeprecated("1990-03-15T15:16:17Z", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637514177); + + ret = ParseISO8601DateTimeDeprecated("1990-03-15t15:16:17z", t); // lower-case must be allowed too + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637514177); + + ret = ParseISO8601DateTimeDeprecated("1990-03-15 15:16:17Z", t); // space as separator should be allowed + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637514177); + + ret = ParseISO8601DateTimeDeprecated("1990-03-15T15:16:17.18Z", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637514177); + + ret = ParseISO8601DateTimeDeprecated("1990-03-15T15:16:17.18+07:32", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637487057); + + ret = ParseISO8601DateTimeDeprecated("1990-03-15T15:16:17.18+0732", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637487057); + + ret = ParseISO8601DateTimeDeprecated("1970-01-01T00:00:00Z", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 0); + + ret = ParseISO8601DateTimeDeprecated("1970-01-01T00:01:02Z", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 62); + +#if 0 + // these tests are disabled, because time zones are handled differently + // in old util/ parser and agalakhov@ parser + ret = ParseISO8601DateTimeDeprecated("1970-01-01", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, -4 * 3600); + + ret = ParseISO8601DateTimeDeprecated("1970-01-02", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 86400 - 3 * 3600); +#endif + + // this is wrong because of timezone + ret = ParseISO8601DateTimeDeprecated("2009-02-14T03:31:30", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 1234567890); + + ret = ParseISO8601DateTimeDeprecated("2009-02-14t03:31:30", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 1234567890); + + ret = ParseISO8601DateTimeDeprecated("2009-02-14T02:31:30+0300", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 1234567890); + + ret = ParseISO8601DateTimeDeprecated("2009-02-14T02:31:30+03:00", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 1234567890); + + ret = ParseISO8601DateTimeDeprecated("2009-02-14 02:31:30+03:00", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 1234567890); + + ret = ParseISO8601DateTimeDeprecated("2010-03-28T04:27:00.000-07:00", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 1269775620); + } + + Y_UNIT_TEST(TestIso8601TimeZoneDeprecated) { + time_t t1, t2, t3, t4; + UNIT_ASSERT(ParseISO8601DateTimeDeprecated("2010-03-28T04:27:00.000+07:00", t1)); + UNIT_ASSERT(ParseISO8601DateTimeDeprecated("2010-03-27T21:27:00.000Z", t2)); + UNIT_ASSERT(ParseISO8601DateTimeDeprecated("2010-03-27T22:27:00.000+0100", t3)); + UNIT_ASSERT(ParseISO8601DateTimeDeprecated("2010-03-27T20:27:00.000-01:00", t4)); + UNIT_ASSERT_VALUES_EQUAL(t1, t2); + UNIT_ASSERT_VALUES_EQUAL(t2, t3); + UNIT_ASSERT_VALUES_EQUAL(t3, t4); + } + + Y_UNIT_TEST(TestIso8601IncorrectDeprecated) { + bool ret; + time_t t; + + t = 12345; + ret = ParseISO8601DateTimeDeprecated("", t); + UNIT_ASSERT(!ret); + UNIT_ASSERT_EQUAL(t, (time_t)12345); + + // some bad dates + t = 54321; + ret = ParseISO8601DateTimeDeprecated("a990-01-15", t); + UNIT_ASSERT(!ret); + UNIT_ASSERT_EQUAL(t, (time_t)54321); + + ret = ParseISO8601DateTimeDeprecated("1970-01-01T03:00:00+04:00", t); // this is 1969 GMT + UNIT_ASSERT(!ret); + + ret = ParseISO8601DateTimeDeprecated("1987-13-16", t); + UNIT_ASSERT(!ret); + + ret = ParseISO8601DateTimeDeprecated("1987-02-29", t); + UNIT_ASSERT(!ret); + + ret = ParseISO8601DateTimeDeprecated("1990-03-151Y15:16:17.18", t); + UNIT_ASSERT(!ret); + + ret = ParseISO8601DateTimeDeprecated("1990-03-151T15:16:17:43.18", t); + UNIT_ASSERT(!ret); + + ret = ParseISO8601DateTimeDeprecated("1990-03-151T15:16:17.18Z+21:32", t); + UNIT_ASSERT(!ret); + } + + Y_UNIT_TEST(TestIso8601FractionsDeprecated) { + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601Deprecated("2009-09-19 03:37:08.1+04:00"), + TInstant::Seconds(1253317028) + TDuration::MilliSeconds(100)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601Deprecated("2009-09-19 03:37:03.926+04:00"), + TInstant::Seconds(1253317023) + TDuration::MilliSeconds(926)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601Deprecated("2009-09-19 03:37:03.92622+04:00"), + TInstant::Seconds(1253317023) + TDuration::MicroSeconds(926220)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601Deprecated("2009-09-19 03:37:03.012331+04:00"), + TInstant::Seconds(1253317023) + TDuration::MicroSeconds(12331)); + } + + Y_UNIT_TEST(TestHttpDateDeprecated) { + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseHttpDeprecated("Sun, 06 Nov 1994 08:49:37 GMT"), + TInstant::ParseIso8601Deprecated("1994-11-06T08:49:37Z")); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseHttpDeprecated("Sunday, 06-Nov-94 08:49:37 GMT"), + TInstant::ParseIso8601Deprecated("1994-11-06T08:49:37Z")); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseHttpDeprecated("Sun Nov 6 08:49:37 1994"), + TInstant::ParseIso8601Deprecated("1994-11-06T08:49:37Z")); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseHttpDeprecated("Mon, 19 Jan 2037 08:49:37 GMT"), + TInstant::ParseIso8601Deprecated("2037-01-19T08:49:37Z")); + } + + Y_UNIT_TEST(TestHttpDateIncorrectDeprecated) { + bool ret; + time_t t = 0; + ret = ParseHTTPDateTimeDeprecated("1990-03-15T15:16:17Z", t); + UNIT_ASSERT(!ret); + } + + Y_UNIT_TEST(TestX509ValidityTimeDeprecated) { + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseX509ValidityDeprecated("20091014165533Z"), + TInstant::ParseRfc822Deprecated("Wed, 14 Oct 2009 16:55:33 GMT")); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseX509ValidityDeprecated("990104074212Z"), + TInstant::ParseRfc822Deprecated("4 Jan 1999 07:42:12 GMT")); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseX509ValidityDeprecated("191231235959Z"), + TInstant::ParseRfc822Deprecated("31 Dec 2019 23:59:59 GMT")); + } + + Y_UNIT_TEST(TestX509ValidityTimeIncorrectDeprecated) { + bool ret; + time_t t = 0; + ret = ParseX509ValidityDateTimeDeprecated("500101000000Z", t); + UNIT_ASSERT(!ret); + ret = ParseX509ValidityDateTimeDeprecated("091014165533+0300", t); + UNIT_ASSERT(!ret); + } + + Y_UNIT_TEST(TestTInstantTryParseDeprecated) { + { + const TStringBuf s = "2009-09-19 03:37:08.1+04:00"; + const auto i = TInstant::ParseIso8601Deprecated(s); + TInstant iTry; + UNIT_ASSERT(TInstant::TryParseIso8601Deprecated(s, iTry)); + UNIT_ASSERT_VALUES_EQUAL(i, iTry); + } + { + const TStringBuf s = "2009-09aslkdjfkljasdjfl4:00"; + TInstant iTry; + UNIT_ASSERT_EXCEPTION(TInstant::ParseIso8601Deprecated(s), TDateTimeParseException); + UNIT_ASSERT(!TInstant::TryParseIso8601Deprecated(s, iTry)); + } + { + const TStringBuf s = "Wed, 14 Oct 2009 16:55:33 GMT"; + const auto i = TInstant::ParseRfc822Deprecated(s); + TInstant iTry; + UNIT_ASSERT(TInstant::TryParseRfc822Deprecated(s, iTry)); + UNIT_ASSERT_VALUES_EQUAL(i, iTry); + } + { + const TStringBuf s = "Wed, alsdjflkasjdfl:55:33 GMT"; + TInstant iTry; + UNIT_ASSERT_EXCEPTION(TInstant::ParseRfc822Deprecated(s), TDateTimeParseException); + UNIT_ASSERT(!TInstant::TryParseRfc822Deprecated(s, iTry)); + } + { + const TStringBuf s = "20091014165533Z"; + const auto i = TInstant::ParseX509ValidityDeprecated(s); + TInstant iTry; + UNIT_ASSERT(TInstant::TryParseX509Deprecated(s, iTry)); + UNIT_ASSERT_VALUES_EQUAL(i, iTry); + } + { + const TStringBuf s = "200asdfasdf533Z"; + TInstant iTry; + UNIT_ASSERT_EXCEPTION(TInstant::ParseX509ValidityDeprecated(s), TDateTimeParseException); + UNIT_ASSERT(!TInstant::TryParseX509Deprecated(s, iTry)); + } + { + const TStringBuf s = "990104074212Z"; + const auto i = TInstant::ParseX509ValidityDeprecated(s); + TInstant iTry; + UNIT_ASSERT(TInstant::TryParseX509Deprecated(s, iTry)); + UNIT_ASSERT_VALUES_EQUAL(i, iTry); + } + { + const TStringBuf s = "9901asdf4212Z"; + TInstant iTry; + UNIT_ASSERT_EXCEPTION(TInstant::ParseX509ValidityDeprecated(s), TDateTimeParseException); + UNIT_ASSERT(!TInstant::TryParseX509Deprecated(s, iTry)); + } + } +} + +Y_UNIT_TEST_SUITE(TDurationParseTestDeprecated) { + Y_UNIT_TEST(TestParseDeprecated) { + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(60), TDuration::Parse("1m")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(90), TDuration::Parse("1.5m")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(102), TDuration::Parse("1.7m")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(119400), TDuration::Parse("1.99m")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(119940), TDuration::Parse("1.999m")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(119994), TDuration::Parse("1.9999m")); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::Minutes(60), TDuration::Parse("1h")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Minutes(90), TDuration::Parse("1.5h")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Minutes(102), TDuration::Parse("1.7h")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(7164), TDuration::Parse("1.99h")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(7196400), TDuration::Parse("1.999h")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(7199640), TDuration::Parse("1.9999h")); + + UNIT_ASSERT_EQUAL(TDuration::Minutes(15), TDuration::Parse("15m")); + UNIT_ASSERT_EQUAL(TDuration::Hours(10), TDuration::Parse("10h")); + UNIT_ASSERT_EQUAL(TDuration::Days(365), TDuration::Parse("365d")); + UNIT_ASSERT_EQUAL(TDuration::Hours(36), TDuration::Parse("1.5d")); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::Hours(24), TDuration::Parse("1d")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Hours(36), TDuration::Parse("1.5d")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Minutes(2448), TDuration::Parse("1.7d")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(171936), TDuration::Parse("1.99d")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(172713600), TDuration::Parse("1.999d")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(172791360), TDuration::Parse("1.9999d")); + +#if 0 // not implemented + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(90), TDuration::Parse("1m30s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Minutes(90), TDuration::Parse("1h30m")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Hours(36), TDuration::Parse("1d12h")); +#endif + + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(10), TDuration::Parse("10s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(10), TDuration::Parse("10.000s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(4), TDuration::Parse("0.000004s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(3456), TDuration::Parse("3.456s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(3450), TDuration::Parse("3.450s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(3450), TDuration::Parse("3.45000000s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(3450), TDuration::Parse("3.45s")); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(1), TDuration::Parse("1ms")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(1100), TDuration::Parse("1.1ms")); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(112), TDuration::Parse("112")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(14456), TDuration::Parse("14456us")); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(1), TDuration::Parse("1000ns")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(1), TDuration::Parse("0.000001s")); + + UNIT_ASSERT_EQUAL(TDuration(), TDuration::Parse("10ns")); // TDuration has 1us precision. + } +} diff --git a/util/datetime/parser_ut.cpp b/util/datetime/parser_ut.cpp new file mode 100644 index 00000000000..6f5c3002280 --- /dev/null +++ b/util/datetime/parser_ut.cpp @@ -0,0 +1,634 @@ +#include "parser.h" + +#include + +static const time_t SECONDS_PER_HOUR = 3600; +static const time_t SECONDS_PER_MINUTE = 60; + +Y_UNIT_TEST_SUITE(TDateTimeParseTest) { + Y_UNIT_TEST(TestRfc822Correct) { + bool r = false; + time_t t = 0; + + // predefined time zones + + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 UT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 GMT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 EST", t); + UNIT_ASSERT(r); + UNIT_ASSERT_VALUES_EQUAL(t, (time_t)1109964885 + 5 * SECONDS_PER_HOUR); + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 EDT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 4 * SECONDS_PER_HOUR); + + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 CST", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 6 * SECONDS_PER_HOUR); + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 CDT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 5 * SECONDS_PER_HOUR); + + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 MST", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 7 * SECONDS_PER_HOUR); + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 MDT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 6 * SECONDS_PER_HOUR); + + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 PST", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 8 * SECONDS_PER_HOUR); + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 PDT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 7 * SECONDS_PER_HOUR); + + // optinal century + r = ParseRFC822DateTime("Fri, 4 Mar 05 19:34:45 UT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + // + optional day of week + r = ParseRFC822DateTime("4 Mar 05 19:34:45 UT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + // + optional seconds + r = ParseRFC822DateTime("4 Mar 05 19:34 UT", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964840); + + // local differential hour+min + + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 +0300", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109954085); + + r = ParseRFC822DateTime("Fri, 4 Mar 05 19:34:45 +0300", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109954085); + + r = ParseRFC822DateTime("21 Apr 1999 23:40:00 +0400", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)924723600); + + r = ParseRFC822DateTime("21 Apr 99 23:40 +0400", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)924723600); + + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 -0300", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 3 * SECONDS_PER_HOUR); + + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34 -0300", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964840 + 3 * SECONDS_PER_HOUR); + + r = ParseRFC822DateTime("Fri, 4 Mar 05 19:34:45 -0330", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 3 * SECONDS_PER_HOUR + 30 * SECONDS_PER_MINUTE); + + r = ParseRFC822DateTime("Fri, 4 Mar 05 19:34 -0330", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964840 + 3 * SECONDS_PER_HOUR + 30 * SECONDS_PER_MINUTE); + + r = ParseRFC822DateTime("4 Mar 2005 19:34:45 -1030", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885 + 10 * SECONDS_PER_HOUR + 30 * SECONDS_PER_MINUTE); + + r = ParseRFC822DateTime("4 Mar 2005 19:34 -1030", t); + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964840 + 10 * SECONDS_PER_HOUR + 30 * SECONDS_PER_MINUTE); + + // white spaces + + r = ParseRFC822DateTime("Fri,4 Mar 2005 19:34:45 UT", t); // no space after comma + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + r = ParseRFC822DateTime(" Fri, 4 Mar 2005 19:34:45 UT ", t); // several spaces, leading and trailing + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + r = ParseRFC822DateTime(" \t Fri, \t 4 \t Mar \t 2005 \t 19:34:45 \t UT \t ", t); // spaces with tabs + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1109964885); + + r = ParseRFC822DateTime("Thu, 01 Jan 1970 03:00:00 +0300", t); // spaces with tabs + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)0); + + r = ParseRFC822DateTime("Sat, 14 Feb 2009 02:31:30 +0300", t); // spaces with tabs + UNIT_ASSERT(r); + UNIT_ASSERT_EQUAL(t, (time_t)1234567890); + } + + time_t GetOffset(char militaryZone) { + char ch = (char)toupper(militaryZone); + if (ch == 'Z') { + return 0; + } else if (ch >= 'A' && ch < 'J') { + return (ch - 'A' + 1) * SECONDS_PER_HOUR; + } else if (ch > 'J' && ch <= 'M') { + return (ch - 'A') * SECONDS_PER_HOUR; + } else if (ch >= 'N' && ch <= 'Y') { + return -(ch - 'N' + 1) * SECONDS_PER_HOUR; + } else { + ythrow yexception() << "Invalid military zone."; + } + } + + void DoTestMilitaryZones(char firstChar, char lastChar) { + const time_t utcTime = 1109964885; // Fri, 4 Mar 2005 19:34:45 UT + char text[] = "Fri, 4 Mar 2005 19:34:45 A"; + const size_t zoneCharIndex = strlen(text) - 1; + + for (char militaryZone = firstChar; militaryZone <= lastChar; ++militaryZone) { + time_t t = 0; + const time_t offset = GetOffset(militaryZone); + // the last character is replaced with next zone symbol + text[zoneCharIndex] = militaryZone; + + UNIT_ASSERT(ParseRFC822DateTime(text, t)); + UNIT_ASSERT_EQUAL(t, utcTime - offset); + } + } + + Y_UNIT_TEST(TestRfc822MilitaryZones) { + DoTestMilitaryZones('A', 'I'); + DoTestMilitaryZones('K', 'Z'); + DoTestMilitaryZones('a', 'i'); + DoTestMilitaryZones('k', 'z'); + } + + Y_UNIT_TEST(TestRfc822IncorrectDates) { + bool r = true; + time_t t = 0; + + t = 12345; + r = ParseRFC822DateTime("", t); + UNIT_ASSERT(!r); + UNIT_ASSERT_EQUAL(t, (time_t)12345); + + t = 223344; + r = ParseRFC822DateTime("Fri, some junk", t); + UNIT_ASSERT(!r); + UNIT_ASSERT_EQUAL(t, (time_t)223344); + + t = 54321; + r = ParseRFC822DateTime("Fri, 4 Mar 2005 19:34:45 UTC", t); + UNIT_ASSERT(!r); + UNIT_ASSERT_EQUAL(t, (time_t)54321); + + // TODO: check semantic validity of parsed date (30 Feb, 88:90 etc.). + // The following tests MUST fail (they don't now) + // r = ParseRFC822DateTime("45 Mar 2005 19:34:45 UT", t); + // UNIT_ASSERT_EQUAL(r, false); + + // r = ParseRFC822DateTime("29 Feb 2005 19:34:45 +0300", t); + // UNIT_ASSERT_EQUAL(r, false); + + // r = ParseRFC822DateTime("31 Apr 2004 19:34:45 +0300", t); + // UNIT_ASSERT_EQUAL(r, false); + + r = ParseRFC822DateTime("17 Nov 2008 19:34:45", t); // no specified time zone + UNIT_ASSERT(!r); + + r = ParseRFC822DateTime("17 Nov 200 19:34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("17 Nov 8 19:34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("17 Nov 20008 19:34:45 UT", t); + UNIT_ASSERT(!r); + + r = ParseRFC822DateTime("17 Nov 2008 1:34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("17 Nov 2008 123:34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("17 Nov 2008 19:1:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("17 Nov 2008 19:123:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("17 Nov 2008 19:34:1 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("17 Nov 2008 19:34:123 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("17 Nov 2008 19:34:12.12 UT", t); // fractions of second are now allowed + UNIT_ASSERT(!r); + + r = ParseRFC822DateTime("Mon , 17 Nov 2005 19:34:45 UT", t); // space after day before the comma + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("Mon, 17 Nov 2005 19 :34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("Mon, 17 Nov 2005 19: 34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("Mon, 17 Nov 2005 19:34 :45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("Mon, 17 Nov 2005 19:34: 45 UT", t); + UNIT_ASSERT(!r); + + r = ParseRFC822DateTime("Monday, 17 Nov 2005 19:34:45 UT", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("Mon, 17 November 2008 19:34:45 UT", t); + UNIT_ASSERT(!r); + + r = ParseRFC822DateTime("Mon, 17 Nov 2008 19:34:45 +3", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("Mon, 17 Nov 2008 19:34:45 +03", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("Mon, 17 Nov 2008 19:34:45 +030", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("Mon, 17 Nov 2008 19:34:45 +03030", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("Mon, 17 Nov 2008 19:34:45 -3", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("Mon, 17 Nov 2008 19:34:45 -03", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("Mon, 17 Nov 2008 19:34:45 -030", t); + UNIT_ASSERT(!r); + r = ParseRFC822DateTime("Mon, 17 Nov 2008 19:34:45 -03030", t); + UNIT_ASSERT(!r); + } + + Y_UNIT_TEST(TestRfc822Partial) { + TRfc822DateTimeParser p; + const char* part1 = "Fri, 4 Mar 05 1"; + const char* part2 = "9:34:45 +0300"; + UNIT_ASSERT(p.ParsePart(part1, strlen(part1))); + UNIT_ASSERT(p.ParsePart(part2, strlen(part2))); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(1109954085), p.GetResult(TInstant::Max())); + p = TRfc822DateTimeParser(); + const char* part3 = "Fri, 4 Mar 05 19:34:46 +0300"; + UNIT_ASSERT(p.ParsePart(part3, strlen(part3))); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(1109954086), p.GetResult(TInstant::Zero())); + } + + Y_UNIT_TEST(TestIso8601Partial) { + TIso8601DateTimeParser p; + const char* part1 = "1990-03-15T15:1"; + const char* part2 = "6:17+0732"; + UNIT_ASSERT(p.ParsePart(part1, strlen(part1))); + UNIT_ASSERT(p.ParsePart(part2, strlen(part2))); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(637487057), p.GetResult(TInstant::Max())); + p = TIso8601DateTimeParser(); + const char* part3 = "1990-03-15T15:16:18+0732"; + UNIT_ASSERT(p.ParsePart(part3, strlen(part3))); + UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(637487058), p.GetResult(TInstant::Zero())); + } + + Y_UNIT_TEST(TestIso8601BeforeEpoch) { + TIso8601DateTimeParser p; + static constexpr TStringBuf timestamp = "0001-01-01T00:00:00Z"; + UNIT_ASSERT(p.ParsePart(timestamp.begin(), timestamp.size())); + UNIT_ASSERT_VALUES_EQUAL(p.GetDateTimeFields().Year, 1); + } + + Y_UNIT_TEST(TestIso8601Correct) { + bool ret; + time_t t; + + // ISO 8601 actually does not allow time without time zone + ret = ParseISO8601DateTime("1990-03-15", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637459200); + + // some normal dates + ret = ParseISO8601DateTime("1990-03-15T15:16:17Z", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637514177); + + ret = ParseISO8601DateTime("1990-03-15t15:16:17z", t); // lower-case must be allowed too + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637514177); + + ret = ParseISO8601DateTime("1990-03-15 15:16:17Z", t); // space as separator should be allowed + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637514177); + + ret = ParseISO8601DateTime("1990-03-15T15:16:17.18Z", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637514177); + + ret = ParseISO8601DateTime("1990-03-15T15:16:17.18+07:32", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637487057); + + ret = ParseISO8601DateTime("1990-03-15T15:16:17.18+0732", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 637487057); + + ret = ParseISO8601DateTime("1970-01-01T00:00:00Z", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 0); + + ret = ParseISO8601DateTime("1970-01-01T00:01:02Z", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 62); + +#if 0 + // these tests are disabled, because time zones are handled differently + // in old util/ parser and agalakhov@ parser + ret = ParseISO8601DateTime("1970-01-01", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, -4 * 3600); + + ret = ParseISO8601DateTime("1970-01-02", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 86400 - 3 * 3600); +#endif + + // this is wrong because of timezone + ret = ParseISO8601DateTime("2009-02-14T03:31:30", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 1234582290); + + ret = ParseISO8601DateTime("2009-02-14t03:31:30", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 1234582290); + + ret = ParseISO8601DateTime("2009-02-14T02:31:30+0300", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 1234567890); + + ret = ParseISO8601DateTime("2009-02-14T02:31:30+03:00", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 1234567890); + + ret = ParseISO8601DateTime("2009-02-14 02:31:30+03:00", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 1234567890); + + ret = ParseISO8601DateTime("2010-03-28T04:27:00.000-07:00", t); + UNIT_ASSERT(ret); + UNIT_ASSERT_VALUES_EQUAL(t, 1269775620); + } + + Y_UNIT_TEST(TestIso8601TimeZone) { + time_t t1, t2, t3, t4; + UNIT_ASSERT(ParseISO8601DateTime("2010-03-28T04:27:00.000+07:00", t1)); + UNIT_ASSERT(ParseISO8601DateTime("2010-03-27T21:27:00.000Z", t2)); + UNIT_ASSERT(ParseISO8601DateTime("2010-03-27T22:27:00.000+0100", t3)); + UNIT_ASSERT(ParseISO8601DateTime("2010-03-27T20:27:00.000-01:00", t4)); + UNIT_ASSERT_VALUES_EQUAL(t1, t2); + UNIT_ASSERT_VALUES_EQUAL(t2, t3); + UNIT_ASSERT_VALUES_EQUAL(t3, t4); + } + + Y_UNIT_TEST(TestIso8601Incorrect) { + bool ret; + time_t t; + + t = 12345; + ret = ParseISO8601DateTime("", t); + UNIT_ASSERT(!ret); + UNIT_ASSERT_EQUAL(t, (time_t)12345); + + // some bad dates + t = 54321; + ret = ParseISO8601DateTime("a990-01-15", t); + UNIT_ASSERT(!ret); + UNIT_ASSERT_EQUAL(t, (time_t)54321); + + ret = ParseISO8601DateTime("1970-01-01T03:00:00+04:00", t); // this is 1969 GMT + UNIT_ASSERT(!ret); + + ret = ParseISO8601DateTime("1987-13-16", t); + UNIT_ASSERT(!ret); + + ret = ParseISO8601DateTime("1987-02-29", t); + UNIT_ASSERT(!ret); + + ret = ParseISO8601DateTime("1990-03-151Y15:16:17.18", t); + UNIT_ASSERT(!ret); + + ret = ParseISO8601DateTime("1990-03-151T15:16:17:43.18", t); + UNIT_ASSERT(!ret); + + ret = ParseISO8601DateTime("1990-03-151T15:16:17.18Z+21:32", t); + UNIT_ASSERT(!ret); + } + + Y_UNIT_TEST(TestIso8601Fractions) { + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("2009-09-19 03:37:08.1+04:00"), + TInstant::Seconds(1253317028) + TDuration::MilliSeconds(100)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("2009-09-19 03:37:03.926+04:00"), + TInstant::Seconds(1253317023) + TDuration::MilliSeconds(926)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("2009-09-19 03:37:03.92622+04:00"), + TInstant::Seconds(1253317023) + TDuration::MicroSeconds(926220)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("2009-09-19 03:37:03.012331+04:00"), + TInstant::Seconds(1253317023) + TDuration::MicroSeconds(12331)); + } + + Y_UNIT_TEST(TestIso8601FractionsBelowMicro) { + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("1970-01-01 00:00:00.0000000+00:00"), + TInstant::Seconds(0)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("1970-01-01 00:00:00.0000009+00:00"), + TInstant::Seconds(0)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("1970-01-01 00:00:00.000000789+00:00"), + TInstant::Seconds(0)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("1970-01-01 00:00:00.1234560+00:00"), + TInstant::Seconds(0) + TDuration::MicroSeconds(123456)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("1970-01-01 00:00:00.1234569+00:00"), + TInstant::Seconds(0) + TDuration::MicroSeconds(123456)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("1970-01-01 00:00:00.123456789+00:00"), + TInstant::Seconds(0) + TDuration::MicroSeconds(123456)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("1970-01-01 00:00:00.9999990+00:00"), + TInstant::Seconds(0) + TDuration::MicroSeconds(999999)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("1970-01-01 00:00:00.9999999+00:00"), + TInstant::Seconds(0) + TDuration::MicroSeconds(999999)); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseIso8601("1970-01-01 00:00:00.999999789+00:00"), + TInstant::Seconds(0) + TDuration::MicroSeconds(999999)); + } + + Y_UNIT_TEST(TestIso8601BigDate) { + TVector> dates{ + {"2019-01-01", 17897}, + + {"2037-01-01", 24472}, + {"2050-01-01", 29220}, + + {"2099-01-01", 47117}, + {"2099-12-31", 47481}, + {"2100-01-01", 47482}, + {"2100-02-28", 47540}, + {"2100-03-01", 47541}, + }; + + for (const auto& [date, days] : dates) { + TInstant instant = TInstant::ParseIso8601(date + "T00:00:00Z"); + UNIT_ASSERT_VALUES_EQUAL(instant.Days(), days); + } + } + + Y_UNIT_TEST(TestHttpDate) { + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseHttp("Sun, 06 Nov 1994 08:49:37 GMT"), + TInstant::ParseIso8601("1994-11-06T08:49:37Z")); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseHttp("Sunday, 06-Nov-94 08:49:37 GMT"), + TInstant::ParseIso8601("1994-11-06T08:49:37Z")); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseHttp("Sun Nov 6 08:49:37 1994"), + TInstant::ParseIso8601("1994-11-06T08:49:37Z")); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseHttp("Mon, 19 Jan 2037 08:49:37 GMT"), + TInstant::ParseIso8601("2037-01-19T08:49:37Z")); + } + + Y_UNIT_TEST(TestHttpDateIncorrect) { + bool ret; + time_t t = 0; + ret = ParseHTTPDateTime("1990-03-15T15:16:17Z", t); + UNIT_ASSERT(!ret); + } + + Y_UNIT_TEST(TestX509ValidityTime) { + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseX509Validity("20091014165533Z"), + TInstant::ParseRfc822("Wed, 14 Oct 2009 16:55:33 GMT")); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseX509Validity("990104074212Z"), + TInstant::ParseRfc822("4 Jan 1999 07:42:12 GMT")); + UNIT_ASSERT_VALUES_EQUAL( + TInstant::ParseX509Validity("191231235959Z"), + TInstant::ParseRfc822("31 Dec 2019 23:59:59 GMT")); + } + + Y_UNIT_TEST(TestX509ValidityTimeIncorrect) { + bool ret; + time_t t = 0; + ret = ParseX509ValidityDateTime("500101000000Z", t); + UNIT_ASSERT(!ret); + ret = ParseX509ValidityDateTime("091014165533+0300", t); + UNIT_ASSERT(!ret); + } + + Y_UNIT_TEST(TestTInstantTryParse) { + { + const TStringBuf s = "2009-09-19 03:37:08.1+04:00"; + const auto i = TInstant::ParseIso8601(s); + TInstant iTry; + UNIT_ASSERT(TInstant::TryParseIso8601(s, iTry)); + UNIT_ASSERT_VALUES_EQUAL(i, iTry); + } + { + const TStringBuf s = "2009-09aslkdjfkljasdjfl4:00"; + TInstant iTry; + UNIT_ASSERT_EXCEPTION(TInstant::ParseIso8601(s), TDateTimeParseException); + UNIT_ASSERT(!TInstant::TryParseIso8601(s, iTry)); + } + { + const TStringBuf s = "Wed, 14 Oct 2009 16:55:33 GMT"; + const auto i = TInstant::ParseRfc822(s); + TInstant iTry; + UNIT_ASSERT(TInstant::TryParseRfc822(s, iTry)); + UNIT_ASSERT_VALUES_EQUAL(i, iTry); + } + { + const TStringBuf s = "Wed, alsdjflkasjdfl:55:33 GMT"; + TInstant iTry; + UNIT_ASSERT_EXCEPTION(TInstant::ParseRfc822(s), TDateTimeParseException); + UNIT_ASSERT(!TInstant::TryParseRfc822(s, iTry)); + } + { + const TStringBuf s = "20091014165533Z"; + const auto i = TInstant::ParseX509Validity(s); + TInstant iTry; + UNIT_ASSERT(TInstant::TryParseX509(s, iTry)); + UNIT_ASSERT_VALUES_EQUAL(i, iTry); + } + { + const TStringBuf s = "200asdfasdf533Z"; + TInstant iTry; + UNIT_ASSERT_EXCEPTION(TInstant::ParseX509Validity(s), TDateTimeParseException); + UNIT_ASSERT(!TInstant::TryParseX509(s, iTry)); + } + { + const TStringBuf s = "990104074212Z"; + const auto i = TInstant::ParseX509Validity(s); + TInstant iTry; + UNIT_ASSERT(TInstant::TryParseX509(s, iTry)); + UNIT_ASSERT_VALUES_EQUAL(i, iTry); + } + { + const TStringBuf s = "9901asdf4212Z"; + TInstant iTry; + UNIT_ASSERT_EXCEPTION(TInstant::ParseX509Validity(s), TDateTimeParseException); + UNIT_ASSERT(!TInstant::TryParseX509(s, iTry)); + } + } +} + +Y_UNIT_TEST_SUITE(TDurationParseTest) { + Y_UNIT_TEST(TestParse) { + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(60 * 60 * 24 * 7), TDuration::Parse("1w")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(60), TDuration::Parse("1m")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(90), TDuration::Parse("1.5m")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(102), TDuration::Parse("1.7m")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(119400), TDuration::Parse("1.99m")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(119940), TDuration::Parse("1.999m")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(119994), TDuration::Parse("1.9999m")); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::Minutes(60), TDuration::Parse("1h")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Minutes(90), TDuration::Parse("1.5h")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Minutes(102), TDuration::Parse("1.7h")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(7164), TDuration::Parse("1.99h")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(7196400), TDuration::Parse("1.999h")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(7199640), TDuration::Parse("1.9999h")); + + UNIT_ASSERT_EQUAL(TDuration::Minutes(15), TDuration::Parse("15m")); + UNIT_ASSERT_EQUAL(TDuration::Hours(10), TDuration::Parse("10h")); + UNIT_ASSERT_EQUAL(TDuration::Days(365), TDuration::Parse("365d")); + UNIT_ASSERT_EQUAL(TDuration::Hours(36), TDuration::Parse("1.5d")); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::Hours(24), TDuration::Parse("1d")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Hours(36), TDuration::Parse("1.5d")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Minutes(2448), TDuration::Parse("1.7d")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(171936), TDuration::Parse("1.99d")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(172713600), TDuration::Parse("1.999d")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(172791360), TDuration::Parse("1.9999d")); + +#if 0 // not implemented + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(90), TDuration::Parse("1m30s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Minutes(90), TDuration::Parse("1h30m")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Hours(36), TDuration::Parse("1d12h")); +#endif + + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(10), TDuration::Parse("10s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(10), TDuration::Parse("10.000s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(4), TDuration::Parse("0.000004s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(3456), TDuration::Parse("3.456s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(3450), TDuration::Parse("3.450s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(3450), TDuration::Parse("3.45000000s")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(3450), TDuration::Parse("3.45s")); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(1), TDuration::Parse("1ms")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(1100), TDuration::Parse("1.1ms")); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(112), TDuration::Parse("112")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(14456), TDuration::Parse("14456us")); + + UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(1), TDuration::Parse("1000ns")); + UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(1), TDuration::Parse("0.000001s")); + + UNIT_ASSERT_EQUAL(TDuration(), TDuration::Parse("10ns")); // TDuration has 1us precision. + } +} diff --git a/util/datetime/process_uptime.cpp b/util/datetime/process_uptime.cpp new file mode 100644 index 00000000000..3d198509372 --- /dev/null +++ b/util/datetime/process_uptime.cpp @@ -0,0 +1,52 @@ +#include "process_uptime.h" +#include "systime.h" +#include "uptime.h" + +#if defined(_linux_) + #include + #include + #include + #include + + #include +#elif defined(_win_) + #include +#endif + +TDuration ProcessUptime() { +#if defined(_win_) + FILETIME createProcessTime; + FILETIME dummy1; + FILETIME dummy2; + FILETIME dummy3; + if (GetProcessTimes(GetCurrentProcess(), &createProcessTime, &dummy1, &dummy2, &dummy3)) { + timeval processCreationTimeval{.tv_sec = 0, .tv_usec = 0}; + FileTimeToTimeval(&createProcessTime, &processCreationTimeval); + const TInstant processCreationInstant = TInstant::Seconds(processCreationTimeval.tv_sec) + TDuration::MicroSeconds(processCreationTimeval.tv_usec); + return TInstant::Now() - processCreationInstant; + } + ythrow TSystemError() << "Failed to obtain process starttime"; +#elif defined(_linux_) + static const auto statPath = "/proc/self/stat"; + // /proc//stat format: #21 (0-based) item - starttime %llu - The time the process started after system boot + TUnbufferedFileInput statFile(statPath); + auto statStr = statFile.ReadAll(); + const auto completeStatsSize = 20; // First two fields skipped to ignore variations of parentheses + const TVector stats = StringSplitter(TStringBuf{statStr}.RAfter(')').After(' ')).Split(' ').Take(completeStatsSize); + ui64 startTimeTicks = 0; + Y_THROW_UNLESS(stats.size() == completeStatsSize, "Broken format of " << statPath); + if (!TryFromString(stats.back(), startTimeTicks)) { + ythrow yexception() << "Failed to extract process starttime value from " << statPath; + } + long ticksPerSecond = sysconf(_SC_CLK_TCK); + Y_THROW_UNLESS_EX(ticksPerSecond != -1, TSystemError() << "Failed to get _SC_CLK_TCK"); + Y_THROW_UNLESS(ticksPerSecond > 0, "Invalid value of the _SC_CLK_TCK variable: " << ticksPerSecond); + const ui64 startTimeSeconds = startTimeTicks / ticksPerSecond; + const ui64 fractionTicks = startTimeTicks % ticksPerSecond; + const TDuration startTimeFractionSeconds = TDuration::MicroSeconds(1'000'000u * fractionTicks / ticksPerSecond); + const TDuration startTime = TDuration::Seconds(startTimeSeconds) + startTimeFractionSeconds; + return Uptime() - startTime; +#else + ythrow yexception() << "unimplemented"; +#endif +} diff --git a/util/datetime/process_uptime.h b/util/datetime/process_uptime.h new file mode 100644 index 00000000000..7f279f0e33b --- /dev/null +++ b/util/datetime/process_uptime.h @@ -0,0 +1,8 @@ +#pragma once + +#include "base.h" + +/** + * Returns process uptime + */ +TDuration ProcessUptime(); diff --git a/util/datetime/process_uptime_ut.cpp b/util/datetime/process_uptime_ut.cpp new file mode 100644 index 00000000000..4ef95e4323a --- /dev/null +++ b/util/datetime/process_uptime_ut.cpp @@ -0,0 +1,17 @@ +#include + +#include "base.h" +#include "process_uptime.h" +#include "uptime.h" + +Y_UNIT_TEST_SUITE(TestProcessUptimeSuite) { + Y_UNIT_TEST(TestProcessUptime) { + auto t0 = Uptime(); + auto t1 = ProcessUptime(); + UNIT_ASSERT(t1 < TDuration::Minutes(30)); + UNIT_ASSERT(t0 > t1); + Sleep(TDuration::MilliSeconds(50)); // typical uptime resolution is 10-16 ms + auto t2 = ProcessUptime(); + UNIT_ASSERT(t2 >= t1); + } +} diff --git a/util/datetime/strptime.cpp b/util/datetime/strptime.cpp new file mode 100644 index 00000000000..f0d4ec333e8 --- /dev/null +++ b/util/datetime/strptime.cpp @@ -0,0 +1,627 @@ +// ATTN! this is port of FreeBSD LIBC code to Win32 - just for convenience. +// Locale is ignored!!! + +/* + * Powerdog Industries kindly requests feedback from anyone modifying + * this function: + * + * Date: Thu, 05 Jun 1997 23:17:17 -0400 + * From: Kevin Ruddy + * To: James FitzGibbon + * Subject: Re: Use of your strptime(3) code (fwd) + * + * The reason for the "no mod" clause was so that modifications would + * come back and we could integrate them and reissue so that a wider + * audience could use it (thereby spreading the wealth). This has + * made it possible to get strptime to work on many operating systems. + * I'm not sure why that's "plain unacceptable" to the FreeBSD team. + * + * Anyway, you can change it to "with or without modification" as + * you see fit. Enjoy. + * + * Kevin Ruddy + * Powerdog Industries, Inc. + */ +/* + * Copyright (c) 1994 Powerdog Industries. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgement: + * This product includes software developed by Powerdog Industries. + * 4. The name of Powerdog Industries may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "systime.h" +#ifdef _win32_ + #ifndef lint + #ifndef NOID +static char copyright[] = + "@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved."; +static char sccsid[] = "@(#)strptime.c 0.1 (Powerdog) 94/03/27"; + #endif /* !defined NOID */ + #endif /* not lint */ + //__FBSDID("$FreeBSD: src/lib/libc/stdtime/strptime.c,v 1.35 2003/11/17 04:19:15 nectar Exp $"); + + //#include "namespace.h" + #include + #include + #include + #include + #include +//#include +//#include "un-namespace.h" +//#include "libc_private.h" + +// ******************* #include "timelocal.h" ********************* +struct lc_time_T { + const char* mon[12]; + const char* month[12]; + const char* wday[7]; + const char* weekday[7]; + const char* X_fmt; + const char* x_fmt; + const char* c_fmt; + const char* am; + const char* pm; + const char* date_fmt; + const char* alt_month[12]; + const char* md_order; + const char* ampm_fmt; +}; + +// ******************* timelocal.c ****************** +/*- + * Copyright (c) 2001 Alexey Zelkin + * Copyright (c) 1997 FreeBSD Inc. + * All rights reserved. +*/ +static const struct lc_time_T _C_time_locale = { + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}, + {"January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"}, + {"Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}, + {"Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"}, + + /* X_fmt */ + "%H:%M:%S", + + /* + * x_fmt + * Since the C language standard calls for + * "date, using locale's date format," anything goes. + * Using just numbers (as here) makes Quakers happier; + * it's also compatible with SVR4. + */ + "%m/%d/%y", + + /* + * c_fmt + */ + "%a %b %e %H:%M:%S %Y", + + /* am */ + "AM", + + /* pm */ + "PM", + + /* date_fmt */ + "%a %b %e %H:%M:%S %Z %Y", + + /* alt_month + * Standalone months forms for %OB + */ + { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"}, + + /* md_order + * Month / day order in dates + */ + "md", + + /* ampm_fmt + * To determine 12-hour clock format time (empty, if N/A) + */ + "%I:%M:%S %p"}; + +struct lc_time_T* +__get_current_time_locale(void) +{ + return /*(_time_using_locale + ? &_time_locale + :*/ + (struct lc_time_T*)&_C_time_locale /*)*/; +} + +// ******************* strptime.c ******************* +static char* _strptime(const char*, const char*, struct tm*, int*); + + #define asizeof(a) (sizeof(a) / sizeof((a)[0])) + + #if defined(_MSC_VER) && (_MSC_VER >= 1900) + #define tzname _tzname + #endif + +static char* +_strptime(const char* buf, const char* fmt, struct tm* tm, int* GMTp) +{ + char c; + const char* ptr; + int i; + size_t len = 0; + int Ealternative, Oalternative; + struct lc_time_T* tptr = __get_current_time_locale(); + + ptr = fmt; + while (*ptr != 0) { + if (*buf == 0) + break; + + c = *ptr++; + + if (c != '%') { + if (isspace((unsigned char)c)) + while (*buf != 0 && isspace((unsigned char)*buf)) + ++buf; + else if (c != *buf++) + return 0; + continue; + } + + Ealternative = 0; + Oalternative = 0; + label: + c = *ptr++; + switch (c) { + case 0: + case '%': + if (*buf++ != '%') + return 0; + break; + + case '+': + buf = _strptime(buf, tptr->date_fmt, tm, GMTp); + if (buf == 0) + return 0; + break; + + case 'C': + if (!isdigit((unsigned char)*buf)) + return 0; + + /* XXX This will break for 3-digit centuries. */ + len = 2; + for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { + i *= 10; + i += *buf - '0'; + --len; + } + if (i < 19) + return 0; + + tm->tm_year = i * 100 - 1900; + break; + + case 'c': + buf = _strptime(buf, tptr->c_fmt, tm, GMTp); + if (buf == 0) + return 0; + break; + + case 'D': + buf = _strptime(buf, "%m/%d/%y", tm, GMTp); + if (buf == 0) + return 0; + break; + + case 'E': + if (Ealternative || Oalternative) + break; + ++Ealternative; + goto label; + + case 'O': + if (Ealternative || Oalternative) + break; + ++Oalternative; + goto label; + + case 'F': + buf = _strptime(buf, "%Y-%m-%d", tm, GMTp); + if (buf == 0) + return 0; + break; + + case 'R': + buf = _strptime(buf, "%H:%M", tm, GMTp); + if (buf == 0) + return 0; + break; + + case 'r': + buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp); + if (buf == 0) + return 0; + break; + + case 'T': + buf = _strptime(buf, "%H:%M:%S", tm, GMTp); + if (buf == 0) + return 0; + break; + + case 'X': + buf = _strptime(buf, tptr->X_fmt, tm, GMTp); + if (buf == 0) + return 0; + break; + + case 'x': + buf = _strptime(buf, tptr->x_fmt, tm, GMTp); + if (buf == 0) + return 0; + break; + + case 'j': + if (!isdigit((unsigned char)*buf)) + return 0; + + len = 3; + for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { + i *= 10; + i += *buf - '0'; + --len; + } + if (i < 1 || i > 366) + return 0; + + tm->tm_yday = i - 1; + break; + + case 'M': + case 'S': + if (*buf == 0 || isspace((unsigned char)*buf)) + break; + + if (!isdigit((unsigned char)*buf)) + return 0; + + len = 2; + for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { + i *= 10; + i += *buf - '0'; + --len; + } + + if (c == 'M') { + if (i > 59) + return 0; + tm->tm_min = i; + } else { + if (i > 60) + return 0; + tm->tm_sec = i; + } + + if (*buf != 0 && isspace((unsigned char)*buf)) + while (*ptr != 0 && !isspace((unsigned char)*ptr)) + ++ptr; + break; + + case 'H': + case 'I': + case 'k': + case 'l': + /* + * Of these, %l is the only specifier explicitly + * documented as not being zero-padded. However, + * there is no harm in allowing zero-padding. + * + * XXX The %l specifier may gobble one too many + * digits if used incorrectly. + */ + if (!isdigit((unsigned char)*buf)) + return 0; + + len = 2; + for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { + i *= 10; + i += *buf - '0'; + --len; + } + if (c == 'H' || c == 'k') { + if (i > 23) + return 0; + } else if (i > 12) + return 0; + + tm->tm_hour = i; + + if (*buf != 0 && isspace((unsigned char)*buf)) + while (*ptr != 0 && !isspace((unsigned char)*ptr)) + ++ptr; + break; + + case 'p': + /* + * XXX This is bogus if parsed before hour-related + * specifiers. + */ + len = strlen(tptr->am); + if (strnicmp(buf, tptr->am, len) == 0) { + if (tm->tm_hour > 12) + return 0; + if (tm->tm_hour == 12) + tm->tm_hour = 0; + buf += len; + break; + } + + len = strlen(tptr->pm); + if (strnicmp(buf, tptr->pm, len) == 0) { + if (tm->tm_hour > 12) + return 0; + if (tm->tm_hour != 12) + tm->tm_hour += 12; + buf += len; + break; + } + + return 0; + + case 'A': + case 'a': + for (i = 0; i < asizeof(tptr->weekday); i++) { + len = strlen(tptr->weekday[i]); + if (strnicmp(buf, tptr->weekday[i], + len) == 0) + break; + len = strlen(tptr->wday[i]); + if (strnicmp(buf, tptr->wday[i], + len) == 0) + break; + } + if (i == asizeof(tptr->weekday)) + return 0; + + tm->tm_wday = i; + buf += len; + break; + + case 'U': + case 'W': + /* + * XXX This is bogus, as we can not assume any valid + * information present in the tm structure at this + * point to calculate a real value, so just check the + * range for now. + */ + if (!isdigit((unsigned char)*buf)) + return 0; + + len = 2; + for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { + i *= 10; + i += *buf - '0'; + --len; + } + if (i > 53) + return 0; + + if (*buf != 0 && isspace((unsigned char)*buf)) + while (*ptr != 0 && !isspace((unsigned char)*ptr)) + ++ptr; + break; + + case 'w': + if (!isdigit((unsigned char)*buf)) + return 0; + + i = *buf - '0'; + if (i > 6) + return 0; + + tm->tm_wday = i; + + if (*buf != 0 && isspace((unsigned char)*buf)) + while (*ptr != 0 && !isspace((unsigned char)*ptr)) + ++ptr; + break; + + case 'd': + case 'e': + /* + * The %e specifier is explicitly documented as not + * being zero-padded but there is no harm in allowing + * such padding. + * + * XXX The %e specifier may gobble one too many + * digits if used incorrectly. + */ + if (!isdigit((unsigned char)*buf)) + return 0; + + len = 2; + for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { + i *= 10; + i += *buf - '0'; + --len; + } + if (i > 31) + return 0; + + tm->tm_mday = i; + + if (*buf != 0 && isspace((unsigned char)*buf)) + while (*ptr != 0 && !isspace((unsigned char)*ptr)) + ++ptr; + break; + + case 'B': + case 'b': + case 'h': + for (i = 0; i < asizeof(tptr->month); i++) { + if (Oalternative) { + if (c == 'B') { + len = strlen(tptr->alt_month[i]); + if (strnicmp(buf, + tptr->alt_month[i], + len) == 0) + break; + } + } else { + len = strlen(tptr->month[i]); + if (strnicmp(buf, tptr->month[i], + len) == 0) + break; + len = strlen(tptr->mon[i]); + if (strnicmp(buf, tptr->mon[i], + len) == 0) + break; + } + } + if (i == asizeof(tptr->month)) + return 0; + + tm->tm_mon = i; + buf += len; + break; + + case 'm': + if (!isdigit((unsigned char)*buf)) + return 0; + + len = 2; + for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { + i *= 10; + i += *buf - '0'; + --len; + } + if (i < 1 || i > 12) + return 0; + + tm->tm_mon = i - 1; + + if (*buf != 0 && isspace((unsigned char)*buf)) + while (*ptr != 0 && !isspace((unsigned char)*ptr)) + ++ptr; + break; + + case 's': { + char* cp; + int sverrno; + long n; + time_t t; + + sverrno = errno; + errno = 0; + n = strtol(buf, &cp, 10); + if (errno == ERANGE || (long)(t = n) != n) { + errno = sverrno; + return 0; + } + errno = sverrno; + buf = cp; + GmTimeR(&t, tm); + *GMTp = 1; + } break; + + case 'Y': + case 'y': + if (*buf == 0 || isspace((unsigned char)*buf)) + break; + + if (!isdigit((unsigned char)*buf)) + return 0; + + len = (c == 'Y') ? 4 : 2; + for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { + i *= 10; + i += *buf - '0'; + --len; + } + if (c == 'Y') + i -= 1900; + if (c == 'y' && i < 69) + i += 100; + if (i < 0) + return 0; + + tm->tm_year = i; + + if (*buf != 0 && isspace((unsigned char)*buf)) + while (*ptr != 0 && !isspace((unsigned char)*ptr)) + ++ptr; + break; + + case 'Z': { + const char* cp; + char* zonestr; + + for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) { /*empty*/ + } + if (cp - buf) { + zonestr = (char*)alloca(cp - buf + 1); + strncpy(zonestr, buf, cp - buf); + zonestr[cp - buf] = '\0'; + tzset(); + if (0 == strcmp(zonestr, "GMT")) { + *GMTp = 1; + } else if (0 == strcmp(zonestr, tzname[0])) { + tm->tm_isdst = 0; + } else if (0 == strcmp(zonestr, tzname[1])) { + tm->tm_isdst = 1; + } else { + return 0; + } + buf += cp - buf; + } + } break; + } + } + return (char*)buf; +} + +char* strptime(const char* buf, const char* fmt, struct tm* tm) +{ + char* ret; + int gmt; + + gmt = 0; + ret = _strptime(buf, fmt, tm, &gmt); + if (ret && gmt) { + time_t t = timegm(tm); + localtime_r(&t, tm); + } + + return (ret); +} +#endif //_win32_ diff --git a/util/datetime/systime.cpp b/util/datetime/systime.cpp new file mode 100644 index 00000000000..fe1d7b99d6e --- /dev/null +++ b/util/datetime/systime.cpp @@ -0,0 +1,294 @@ +#include "systime.h" + +#include +#include + +#ifdef _win_ + +namespace { + // Number of 100 nanosecond units from 1/1/1601 to 1/1/1970 + constexpr ui64 NUMBER_OF_100_NANO_BETWEEN_1601_1970 = + ULL(116444736000000000); + constexpr ui64 NUMBER_OF_100_NANO_IN_SECOND = ULL(10000000); + + union TFTUnion { + ui64 FTScalar; + FILETIME FTStruct; + }; +} // namespace + +void FileTimeToTimeval(const FILETIME* ft, timeval* tv) { + Y_ASSERT(ft); + Y_ASSERT(tv); + TFTUnion ntTime; + ntTime.FTStruct = *ft; + ntTime.FTScalar -= NUMBER_OF_100_NANO_BETWEEN_1601_1970; + tv->tv_sec = + static_cast(ntTime.FTScalar / NUMBER_OF_100_NANO_IN_SECOND); + tv->tv_usec = static_cast( + (ntTime.FTScalar % NUMBER_OF_100_NANO_IN_SECOND) / LL(10)); +} + +void FileTimeToTimespec(const FILETIME& ft, struct timespec* ts) { + Y_ASSERT(ts); + TFTUnion ntTime; + ntTime.FTStruct = ft; + ntTime.FTScalar -= NUMBER_OF_100_NANO_BETWEEN_1601_1970; + ts->tv_sec = + static_cast(ntTime.FTScalar / NUMBER_OF_100_NANO_IN_SECOND); + ts->tv_nsec = static_cast( + (ntTime.FTScalar % NUMBER_OF_100_NANO_IN_SECOND) * LL(100)); +} + +int gettimeofday(timeval* tp, void*) { + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + FileTimeToTimeval(&ft, tp); + return 0; +} + +tm* localtime_r(const time_t* clock, tm* result) { + tzset(); + tm* res = localtime(clock); + if (res) { + memcpy(result, res, sizeof(tm)); + return result; + } + return 0; +} + +tm* gmtime_r(const time_t* clock, tm* result) { + return gmtime_s(result, clock) == 0 ? result : 0; +} + +char* ctime_r(const time_t* clock, char* buf) { + char* res = ctime(clock); + if (res) { + memcpy(buf, res, 26); + return buf; + } + return 0; +} + +#endif /* _win_ */ + +namespace { + constexpr int STRUCT_TM_BASE_YEAR = 1900; + constexpr int UNIX_TIME_BASE_YEAR = 1970; + constexpr long SECONDS_PER_DAY = (24L * 60L * 60L); + + constexpr bool IsLeapYear(int year) { + if (year % 4 != 0) { + return false; + } + if (year % 100 != 0) { + return true; + } + return year % 400 == 0; + } + + constexpr ui16 DAYS_IN_YEAR = 365; + constexpr ui16 DAYS_IN_LEAP_YEAR = 366; + + constexpr ui16 YearSize(int year) { + return IsLeapYear(year) ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR; + } + + constexpr ui64 FOUR_CENTURIES = (400 * 365 + 100 - 3); + + constexpr ui16 MONTH_TO_DAYS[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + + constexpr ui16 MONTH_TO_DAYS_LEAP[12] = { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}; + + template + constexpr int DayOfYearToMonth(ui64& day) { + Y_ASSERT(day >= 0); + Y_ASSERT(day < 366); + + constexpr ui8 JanDays = 31; + constexpr ui8 FebDays = JanDays + DaysInFeb; + constexpr ui8 MarDays = FebDays + 31; + constexpr ui8 AprDays = MarDays + 30; + constexpr ui8 MayDays = AprDays + 31; + constexpr ui8 JunDays = MayDays + 30; + constexpr ui8 JulDays = JunDays + 31; + constexpr ui16 AugDays = JulDays + 31; + constexpr ui16 SepDays = AugDays + 30; + constexpr ui16 OctDays = SepDays + 31; + constexpr ui16 NovDays = OctDays + 30; + + // hard-coded binary search + // this approach is faster that lookup in array using std::lower_bound() + // GmTimeR takes ~40 cycles vs ~60 cycles using std::lower_bound version + if (day < JunDays) { + if (day < MarDays) { + if (day < JanDays) { + return 0; + } else if (day < FebDays) { + day -= JanDays; + return 1; + } else { + day -= FebDays; + return 2; + } + } else { + if (day < AprDays) { + day -= MarDays; + return 3; + } else if (day < MayDays) { + day -= AprDays; + return 4; + } else { + day -= MayDays; + return 5; + } + } + } else { + if (day < SepDays) { + if (day < JulDays) { + day -= JunDays; + return 6; + } else if (day < AugDays) { + day -= JulDays; + return 7; + } else { + day -= AugDays; + return 8; + } + } else { + if (day < OctDays) { + day -= SepDays; + return 9; + } else if (day < NovDays) { + day -= OctDays; + return 10; + } else { + day -= NovDays; + return 11; + } + } + } + } + + class TDayNoToYearLookupTable { + private: + static constexpr int TableSize = 128; + // lookup table for years in [1970, 1970 + 128 = 2098] range + ui16 DaysSinceEpoch[TableSize] = {}; + + public: + constexpr TDayNoToYearLookupTable() { + DaysSinceEpoch[0] = YearSize(UNIX_TIME_BASE_YEAR); + + for (int year = UNIX_TIME_BASE_YEAR + 1; year < UNIX_TIME_BASE_YEAR + TableSize; ++year) { + DaysSinceEpoch[year - UNIX_TIME_BASE_YEAR] = DaysSinceEpoch[year - UNIX_TIME_BASE_YEAR - 1] + YearSize(year); + } + } + + // lookup year by days since epoch, decrement day counter to the corresponding amount of days. + // The method returns the last year in the table, if year is too big + int GetYear(ui64& days) const { + size_t year = std::upper_bound(DaysSinceEpoch, Y_ARRAY_END(DaysSinceEpoch), days) - Y_ARRAY_BEGIN(DaysSinceEpoch); + if (year > 0) { + days -= DaysSinceEpoch[year - 1]; + } + + return year + UNIX_TIME_BASE_YEAR; + } + }; + + constexpr TDayNoToYearLookupTable DAYS_TO_YEAR_LOOKUP; +} + +//! Inverse of gmtime: converts struct tm to time_t, assuming the data +//! in tm is UTC rather than local timezone. This implementation +//! returns the number of seconds since 1970-01-01, converted to time_t. +//! @note this code adopted from +//! http://osdir.com/ml/web.wget.patches/2005-07/msg00010.html +//! Subject: A more robust timegm - msg#00010 +time_t TimeGM(const struct tm* t) { + // Only handles years after 1970 + if (Y_UNLIKELY(t->tm_year < 70)) { + return (time_t)-1; + } + + int days = 365 * (t->tm_year - 70); + // Take into account the leap days between 1970 and YEAR-1 + days += (t->tm_year - 1 - 68) / 4 - ((t->tm_year - 1) / 100) + ((t->tm_year - 1 + 300) / 400); + + if (Y_UNLIKELY(t->tm_mon < 0 || t->tm_mon >= 12)) { + return (time_t)-1; + } + if (IsLeapYear(1900 + t->tm_year)) { + days += MONTH_TO_DAYS_LEAP[t->tm_mon]; + } else { + days += MONTH_TO_DAYS[t->tm_mon]; + } + + days += t->tm_mday - 1; + + unsigned long secs = days * 86400ul + t->tm_hour * 3600 + t->tm_min * 60 + t->tm_sec; + return (time_t)secs; +} + +struct tm* GmTimeR(const time_t* timer, struct tm* tmbuf) { + i64 time = static_cast(*timer); + + ui64 dayclock, dayno; + int year = UNIX_TIME_BASE_YEAR; + + if (Y_UNLIKELY(time < 0)) { + ui64 shift = (ui64)(-time - 1) / (FOUR_CENTURIES * SECONDS_PER_DAY) + 1; + time += shift * (FOUR_CENTURIES * SECONDS_PER_DAY); + year -= shift * 400; + } + + dayclock = (ui64)time % SECONDS_PER_DAY; + dayno = (ui64)time / SECONDS_PER_DAY; + + if (Y_UNLIKELY(dayno >= FOUR_CENTURIES)) { + year += 400 * (dayno / FOUR_CENTURIES); + dayno = dayno % FOUR_CENTURIES; + } + + tmbuf->tm_sec = dayclock % 60; + tmbuf->tm_min = (dayclock % 3600) / 60; + tmbuf->tm_hour = dayclock / 3600; + tmbuf->tm_wday = (dayno + 4) % 7; // Day 0 was a thursday + + if (Y_LIKELY(year == UNIX_TIME_BASE_YEAR)) { + year = DAYS_TO_YEAR_LOOKUP.GetYear(dayno); + } + + for (;;) { + const ui16 yearSize = YearSize(year); + if (dayno < yearSize) { + break; + } + dayno -= yearSize; + ++year; + } + + tmbuf->tm_year = year - STRUCT_TM_BASE_YEAR; + tmbuf->tm_yday = dayno; + tmbuf->tm_mon = IsLeapYear(year) + ? DayOfYearToMonth<29>(dayno) + : DayOfYearToMonth<28>(dayno); + tmbuf->tm_mday = dayno + 1; + tmbuf->tm_isdst = 0; +#ifndef _win_ + tmbuf->tm_gmtoff = 0; + tmbuf->tm_zone = (char*)"UTC"; +#endif + + return tmbuf; +} + +TString CTimeR(const time_t* timer) { + char sTime[32]; + sTime[0] = 0; + ctime_r(timer, &sTime[0]); + return sTime; +} diff --git a/util/datetime/systime.h b/util/datetime/systime.h new file mode 100644 index 00000000000..760c28d6e67 --- /dev/null +++ b/util/datetime/systime.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +#include + +// timegm and gmtime_r versions that don't need access to filesystem or a big stack +time_t TimeGM(const struct tm* t); +struct tm* GmTimeR(const time_t* timer, struct tm* tmbuf); +// safe version of ctime, convinient version of ctime_r +TString CTimeR(const time_t* timer); + +#ifdef _win_ + #include + #include + +// Convert FILETIME to timeval - seconds and microseconds. +void FileTimeToTimeval(const FILETIME* ft, struct timeval* tv); + +// Convert FILETIME to timespec - seconds and nanoseconds. +void FileTimeToTimespec(const FILETIME& ft, struct timespec* ts); + +// obtains the current time, expressed as seconds and microseconds since 00:00 UTC, January 1, 1970 +int gettimeofday(struct timeval* tp, void*); + +// thou should not mix these with non-_r functions +tm* localtime_r(const time_t* clock, tm* result); +tm* gmtime_r(const time_t* clock, tm* result); +char* ctime_r(const time_t* clock, char* buf); + +inline time_t timegm(struct tm* t) { + return TimeGM(t); +} + +char* strptime(const char* buf, const char* fmt, struct tm* tm); // strptime.cpp +#else + #include +#endif + +#ifndef timersub + #define timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) +#endif diff --git a/util/datetime/uptime.cpp b/util/datetime/uptime.cpp new file mode 100644 index 00000000000..d2e1191c84f --- /dev/null +++ b/util/datetime/uptime.cpp @@ -0,0 +1,60 @@ +#include "uptime.h" + +#if defined(_win_) + #include +#elif defined(_linux_) + #include + #include +#elif defined(_darwin_) + #include +#endif + +#if defined(_darwin_) +namespace { + TInstant GetBootTime() { + struct timeval timeSinceBoot; + size_t len = sizeof(timeSinceBoot); + int request[2] = {CTL_KERN, KERN_BOOTTIME}; + if (sysctl(request, 2, &timeSinceBoot, &len, nullptr, 0) < 0) { + ythrow yexception() << "cannot get kern.boottime from sysctl"; + } + return TInstant::MicroSeconds(timeSinceBoot.tv_sec * 1'000'000 + timeSinceBoot.tv_usec); + } + + TDuration GetDarwinUptime() { + TInstant beforeNow; + TInstant afterNow; + TInstant now; + + // avoid race when NTP changes machine time between getting Now() and uptime + afterNow = GetBootTime(); + do { + beforeNow = afterNow; + now = Now(); + afterNow = GetBootTime(); + } while (afterNow != beforeNow); + + return now - beforeNow; + } +} +#endif // _darwin_ + +TDuration Uptime() { +#if defined(_win_) + return TDuration::MilliSeconds(GetTickCount64()); +#elif defined(_linux_) + TUnbufferedFileInput in("/proc/uptime"); + TString uptimeStr = in.ReadLine(); + double up, idle; + if (sscanf(uptimeStr.data(), "%lf %lf", &up, &idle) < 2) { + ythrow yexception() << "cannot read values from /proc/uptime"; + } + return TDuration::MilliSeconds(up * 1000.0); +#elif defined(_darwin_) + return GetDarwinUptime(); +#elif defined(_emscripten_) + ythrow yexception() << "unimplemented"; +#else + #error "Implement this method" +#endif +} diff --git a/util/datetime/uptime.h b/util/datetime/uptime.h new file mode 100644 index 00000000000..88f0de63dad --- /dev/null +++ b/util/datetime/uptime.h @@ -0,0 +1,8 @@ +#pragma once + +#include "base.h" + +/** + * Returns system uptime + */ +TDuration Uptime(); diff --git a/util/datetime/uptime_ut.cpp b/util/datetime/uptime_ut.cpp new file mode 100644 index 00000000000..7f5ecc482c8 --- /dev/null +++ b/util/datetime/uptime_ut.cpp @@ -0,0 +1,12 @@ +#include + +#include "uptime.h" + +Y_UNIT_TEST_SUITE(TestUptimeSuite) { + Y_UNIT_TEST(TestUptime) { + auto t1 = Uptime(); + Sleep(TDuration::MilliSeconds(50)); // typical uptime resolution is 10-16 ms + auto t2 = Uptime(); + UNIT_ASSERT(t2 > t1); + } +} diff --git a/util/datetime/ut/ya.make b/util/datetime/ut/ya.make new file mode 100644 index 00000000000..617b9fcddb4 --- /dev/null +++ b/util/datetime/ut/ya.make @@ -0,0 +1,14 @@ +UNITTEST_FOR(util) + +SRCS( + datetime/base_ut.cpp + datetime/cputimer_ut.cpp + datetime/parser_deprecated_ut.cpp + datetime/parser_ut.cpp + datetime/process_uptime_ut.cpp + datetime/uptime_ut.cpp +) + +INCLUDE(${ARCADIA_ROOT}/util/tests/ya_util_tests.inc) + +END() diff --git a/util/datetime/ya.make b/util/datetime/ya.make new file mode 100644 index 00000000000..7302cc8ffbe --- /dev/null +++ b/util/datetime/ya.make @@ -0,0 +1,9 @@ +IF (NOT OS_EMSCRIPTEN) + RECURSE( + benchmark + ) +ENDIF() + +RECURSE_FOR_TESTS( + ut +) diff --git a/util/digest/benchmark/murmur/main.cpp b/util/digest/benchmark/murmur/main.cpp new file mode 100644 index 00000000000..ee916bece76 --- /dev/null +++ b/util/digest/benchmark/murmur/main.cpp @@ -0,0 +1,27 @@ +#include + +#include +#include + +#include + +constexpr auto MakeTestData() { + std::array result{}; + for (ui64 i = 0; i < result.size(); ++i) { + result[i] = i; + } + return result; +} + +constexpr auto TEST_DATA = MakeTestData(); + +template +static void BenchmarkMurmurHash(benchmark::State& state) { + for (auto _ : state) { + Result hash = MurmurHash(TEST_DATA.data(), sizeof(TEST_DATA)); + Y_DO_NOT_OPTIMIZE_AWAY(hash); + } +} + +BENCHMARK_TEMPLATE(BenchmarkMurmurHash, ui32); +BENCHMARK_TEMPLATE(BenchmarkMurmurHash, ui64); diff --git a/util/digest/benchmark/murmur/ya.make b/util/digest/benchmark/murmur/ya.make new file mode 100644 index 00000000000..39559996abc --- /dev/null +++ b/util/digest/benchmark/murmur/ya.make @@ -0,0 +1,7 @@ +G_BENCHMARK() + +SRCS( + main.cpp +) + +END() diff --git a/util/digest/benchmark/ya.make b/util/digest/benchmark/ya.make new file mode 100644 index 00000000000..bfc0faff9ec --- /dev/null +++ b/util/digest/benchmark/ya.make @@ -0,0 +1,3 @@ +RECURSE( + murmur +) diff --git a/util/digest/city.cpp b/util/digest/city.cpp new file mode 100644 index 00000000000..c0c7c1a2e93 --- /dev/null +++ b/util/digest/city.cpp @@ -0,0 +1,309 @@ +// Copyright (c) 2011 Google, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// CityHash Version 1, by Geoff Pike and Jyrki Alakuijala + +// This file provides CityHash64() and related functions. + +// It's probably possible to create even faster hash functions by +// writing a program that systematically explores some of the space of +// possible hash functions, by using SIMD instructions, or by +// compromising on hash quality. + +#include "city.h" + +using uint8 = ui8; +using uint32 = ui32; +using uint64 = ui64; + +#include +#include + +using namespace std; + +//#define UNALIGNED_LOAD64(p) (*(const uint64*)(p)) +//#define UNALIGNED_LOAD32(p) (*(const uint32*)(p)) + +#define UNALIGNED_LOAD64(p) (ReadUnaligned((const void*)(p))) +#define UNALIGNED_LOAD32(p) (ReadUnaligned((const void*)(p))) + +#define LIKELY(x) Y_LIKELY(!!(x)) + +// Some primes between 2^63 and 2^64 for various uses. +static const uint64 k0 = 0xc3a5c85c97cb3127ULL; +static const uint64 k1 = 0xb492b66fbe98f273ULL; +static const uint64 k2 = 0x9ae16a3b2f90404fULL; +static const uint64 k3 = 0xc949d7c7509e6557ULL; + +// Bitwise right rotate. Normally this will compile to a single +// instruction, especially if the shift is a manifest constant. +static uint64 Rotate(uint64 val, int shift) { + // Avoid shifting by 64: doing so yields an undefined result. + return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); +} + +// Equivalent to Rotate(), but requires the second arg to be non-zero. +// On x86-64, and probably others, it's possible for this to compile +// to a single instruction if both args are already in registers. +static uint64 RotateByAtLeast1(uint64 val, int shift) { + return (val >> shift) | (val << (64 - shift)); +} + +static uint64 ShiftMix(uint64 val) { + return val ^ (val >> 47); +} + +static uint64 HashLen16(uint64 u, uint64 v) { + return Hash128to64(uint128(u, v)); +} + +static uint64 HashLen0to16(const char* s, size_t len) { + if (len > 8) { + uint64 a = UNALIGNED_LOAD64(s); + uint64 b = UNALIGNED_LOAD64(s + len - 8); + return HashLen16(a, RotateByAtLeast1(b + len, static_cast(len))) ^ b; + } + if (len >= 4) { + uint64 a = UNALIGNED_LOAD32(s); + return HashLen16(len + (a << 3), UNALIGNED_LOAD32(s + len - 4)); + } + if (len > 0) { + uint8 a = s[0]; + uint8 b = s[len >> 1]; + uint8 c = s[len - 1]; + uint32 y = static_cast(a) + (static_cast(b) << 8); + uint32 z = static_cast(len) + (static_cast(c) << 2); + return ShiftMix(y * k2 ^ z * k3) * k2; + } + return k2; +} + +// This probably works well for 16-byte strings as well, but it may be overkill +// in that case. +static uint64 HashLen17to32(const char* s, size_t len) { + uint64 a = UNALIGNED_LOAD64(s) * k1; + uint64 b = UNALIGNED_LOAD64(s + 8); + uint64 c = UNALIGNED_LOAD64(s + len - 8) * k2; + uint64 d = UNALIGNED_LOAD64(s + len - 16) * k0; + return HashLen16(Rotate(a - b, 43) + Rotate(c, 30) + d, + a + Rotate(b ^ k3, 20) - c + len); +} + +// Return a 16-byte hash for 48 bytes. Quick and dirty. +// Callers do best to use "random-looking" values for a and b. +static pair WeakHashLen32WithSeeds( + uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) { + a += w; + b = Rotate(b + a + z, 21); + uint64 c = a; + a += x; + a += y; + b += Rotate(a, 44); + return make_pair(a + z, b + c); +} + +// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. +static pair WeakHashLen32WithSeeds( + const char* s, uint64 a, uint64 b) { + return WeakHashLen32WithSeeds(UNALIGNED_LOAD64(s), + UNALIGNED_LOAD64(s + 8), + UNALIGNED_LOAD64(s + 16), + UNALIGNED_LOAD64(s + 24), + a, + b); +} + +// Return an 8-byte hash for 33 to 64 bytes. +static uint64 HashLen33to64(const char* s, size_t len) { + uint64 z = UNALIGNED_LOAD64(s + 24); + uint64 a = UNALIGNED_LOAD64(s) + (len + UNALIGNED_LOAD64(s + len - 16)) * k0; + uint64 b = Rotate(a + z, 52); + uint64 c = Rotate(a, 37); + a += UNALIGNED_LOAD64(s + 8); + c += Rotate(a, 7); + a += UNALIGNED_LOAD64(s + 16); + uint64 vf = a + z; + uint64 vs = b + Rotate(a, 31) + c; + a = UNALIGNED_LOAD64(s + 16) + UNALIGNED_LOAD64(s + len - 32); + z = UNALIGNED_LOAD64(s + len - 8); + b = Rotate(a + z, 52); + c = Rotate(a, 37); + a += UNALIGNED_LOAD64(s + len - 24); + c += Rotate(a, 7); + a += UNALIGNED_LOAD64(s + len - 16); + uint64 wf = a + z; + uint64 ws = b + Rotate(a, 31) + c; + uint64 r = ShiftMix((vf + ws) * k2 + (wf + vs) * k0); + return ShiftMix(r * k0 + vs) * k2; +} + +uint64 CityHash64(const char* s, size_t len) noexcept { + if (len <= 32) { + if (len <= 16) { + return HashLen0to16(s, len); + } else { + return HashLen17to32(s, len); + } + } else if (len <= 64) { + return HashLen33to64(s, len); + } + + // For strings over 64 bytes we hash the end first, and then as we + // loop we keep 56 bytes of state: v, w, x, y, and z. + uint64 x = UNALIGNED_LOAD64(s); + uint64 y = UNALIGNED_LOAD64(s + len - 16) ^ k1; + uint64 z = UNALIGNED_LOAD64(s + len - 56) ^ k0; + pair v = WeakHashLen32WithSeeds(s + len - 64, len, y); + pair w = WeakHashLen32WithSeeds(s + len - 32, len * k1, k0); + z += ShiftMix(v.second) * k1; + x = Rotate(z + x, 39) * k1; + y = Rotate(y, 33) * k1; + + // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. + len = (len - 1) & ~static_cast(63); + do { + x = Rotate(x + y + v.first + UNALIGNED_LOAD64(s + 16), 37) * k1; + y = Rotate(y + v.second + UNALIGNED_LOAD64(s + 48), 42) * k1; + x ^= w.second; + y ^= v.first; + z = Rotate(z ^ w.first, 33); + v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); + w = WeakHashLen32WithSeeds(s + 32, z + w.second, y); + DoSwap(z, x); + s += 64; + len -= 64; + } while (len != 0); + return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z, + HashLen16(v.second, w.second) + x); +} + +uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) noexcept { + return CityHash64WithSeeds(s, len, k2, seed); +} + +uint64 CityHash64WithSeeds(const char* s, size_t len, + uint64 seed0, uint64 seed1) noexcept { + return HashLen16(CityHash64(s, len) - seed0, seed1); +} + +// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings +// of any length representable in ssize_t. Based on City and Murmur. +static uint128 CityMurmur(const char* s, size_t len, uint128 seed) { + uint64 a = Uint128Low64(seed); + uint64 b = Uint128High64(seed); + uint64 c = 0; + uint64 d = 0; + ssize_t l = len - 16; + if (l <= 0) { // len <= 16 + c = b * k1 + HashLen0to16(s, len); + d = Rotate(a + (len >= 8 ? UNALIGNED_LOAD64(s) : c), 32); + } else { // len > 16 + c = HashLen16(UNALIGNED_LOAD64(s + len - 8) + k1, a); + d = HashLen16(b + len, c + UNALIGNED_LOAD64(s + len - 16)); + a += d; + do { + a ^= ShiftMix(UNALIGNED_LOAD64(s) * k1) * k1; + a *= k1; + b ^= a; + c ^= ShiftMix(UNALIGNED_LOAD64(s + 8) * k1) * k1; + c *= k1; + d ^= c; + s += 16; + l -= 16; + } while (l > 0); + } + a = HashLen16(a, c); + b = HashLen16(d, b); + return uint128(a ^ b, HashLen16(b, a)); +} + +uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) noexcept { + if (len < 128) { + return CityMurmur(s, len, seed); + } + + // We expect len >= 128 to be the common case. Keep 56 bytes of state: + // v, w, x, y, and z. + pair v, w; + uint64 x = Uint128Low64(seed); + uint64 y = Uint128High64(seed); + uint64 z = len * k1; + v.first = Rotate(y ^ k1, 49) * k1 + UNALIGNED_LOAD64(s); + v.second = Rotate(v.first, 42) * k1 + UNALIGNED_LOAD64(s + 8); + w.first = Rotate(y + z, 35) * k1 + x; + w.second = Rotate(x + UNALIGNED_LOAD64(s + 88), 53) * k1; + + // This is the same inner loop as CityHash64(), manually unrolled. + do { + x = Rotate(x + y + v.first + UNALIGNED_LOAD64(s + 16), 37) * k1; + y = Rotate(y + v.second + UNALIGNED_LOAD64(s + 48), 42) * k1; + x ^= w.second; + y ^= v.first; + z = Rotate(z ^ w.first, 33); + v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); + w = WeakHashLen32WithSeeds(s + 32, z + w.second, y); + DoSwap(z, x); + s += 64; + x = Rotate(x + y + v.first + UNALIGNED_LOAD64(s + 16), 37) * k1; + y = Rotate(y + v.second + UNALIGNED_LOAD64(s + 48), 42) * k1; + x ^= w.second; + y ^= v.first; + z = Rotate(z ^ w.first, 33); + v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); + w = WeakHashLen32WithSeeds(s + 32, z + w.second, y); + DoSwap(z, x); + s += 64; + len -= 128; + } while (LIKELY(len >= 128)); + y += Rotate(w.first, 37) * k0 + z; + x += Rotate(v.first + z, 49) * k0; + // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. + for (size_t tail_done = 0; tail_done < len;) { + tail_done += 32; + y = Rotate(y - x, 42) * k0 + v.second; + w.first += UNALIGNED_LOAD64(s + len - tail_done + 16); + x = Rotate(x, 49) * k0 + w.first; + w.first += v.first; + v = WeakHashLen32WithSeeds(s + len - tail_done, v.first, v.second); + } + // At this point our 48 bytes of state should contain more than + // enough information for a strong 128-bit hash. We use two + // different 48-byte-to-8-byte hashes to get a 16-byte final result. + x = HashLen16(x, v.first); + y = HashLen16(y, w.first); + return uint128(HashLen16(x + v.second, w.second) + y, + HashLen16(x + w.second, y + v.second)); +} + +uint128 CityHash128(const char* s, size_t len) noexcept { + if (len >= 16) { + return CityHash128WithSeed(s + 16, + len - 16, + uint128(UNALIGNED_LOAD64(s) ^ k3, + UNALIGNED_LOAD64(s + 8))); + } else if (len >= 8) { + return CityHash128WithSeed(nullptr, + 0, + uint128(UNALIGNED_LOAD64(s) ^ (len * k0), + UNALIGNED_LOAD64(s + len - 8) ^ k1)); + } else { + return CityHash128WithSeed(s, len, uint128(k0, k1)); + } +} diff --git a/util/digest/city.h b/util/digest/city.h new file mode 100644 index 00000000000..675a798074f --- /dev/null +++ b/util/digest/city.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include + +#include + +// NOTE: These functions provide CityHash 1.0 implementation whose results are *different* from +// the mainline version of CityHash. + +using uint128 = std::pair; + +constexpr ui64 Uint128Low64(const uint128& x) { + return x.first; +} + +constexpr ui64 Uint128High64(const uint128& x) { + return x.second; +} + +// Hash functions for a byte array. +// http://en.wikipedia.org/wiki/CityHash + +Y_PURE_FUNCTION ui64 CityHash64(const char* buf, size_t len) noexcept; + +Y_PURE_FUNCTION ui64 CityHash64WithSeed(const char* buf, size_t len, ui64 seed) noexcept; + +Y_PURE_FUNCTION ui64 CityHash64WithSeeds(const char* buf, size_t len, ui64 seed0, ui64 seed1) noexcept; + +Y_PURE_FUNCTION uint128 CityHash128(const char* s, size_t len) noexcept; + +Y_PURE_FUNCTION uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) noexcept; + +// Hash 128 input bits down to 64 bits of output. +// This is intended to be a reasonably good hash function. +inline ui64 Hash128to64(const uint128& x) { + // Murmur-inspired hashing. + const ui64 kMul = 0x9ddfea08eb382d69ULL; + ui64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; + a ^= (a >> 47); + ui64 b = (Uint128High64(x) ^ a) * kMul; + b ^= (b >> 47); + b *= kMul; + return b; +} + +namespace NPrivateCityHash { + template + inline TStringBuf GetBufFromStr(const TStringType& str) { + static_assert(std::is_integral>::value, "invalid type passed to hash function"); + return TStringBuf(reinterpret_cast(str.data()), (str.size()) * sizeof(*str.data())); + } +} + +template +inline ui64 CityHash64(const TStringType& str) { + TStringBuf buf = NPrivateCityHash::GetBufFromStr(str); + return CityHash64(buf.data(), buf.size()); +} + +template +inline ui64 CityHash64WithSeeds(const TStringType& str, ui64 seed0, ui64 seed1) { + TStringBuf buf = NPrivateCityHash::GetBufFromStr(str); + return CityHash64WithSeeds(buf.data(), buf.size(), seed0, seed1); +} + +template +inline ui64 CityHash64WithSeed(const TStringType& str, ui64 seed) { + TStringBuf buf = NPrivateCityHash::GetBufFromStr(str); + return CityHash64WithSeed(buf.data(), buf.size(), seed); +} + +template +inline uint128 CityHash128(const TStringType& str) { + TStringBuf buf = NPrivateCityHash::GetBufFromStr(str); + return CityHash128(buf.data(), buf.size()); +} + +template +inline uint128 CityHash128WithSeed(const TStringType& str, uint128 seed) { + TStringBuf buf = NPrivateCityHash::GetBufFromStr(str); + return CityHash128WithSeed(buf.data(), buf.size(), seed); +} diff --git a/util/digest/city_ut.cpp b/util/digest/city_ut.cpp new file mode 100644 index 00000000000..972091c2c3d --- /dev/null +++ b/util/digest/city_ut.cpp @@ -0,0 +1,15 @@ +#include "city.h" + +#include + +Y_UNIT_TEST_SUITE(TCityTest) { + Y_UNIT_TEST(TestTemplatesCompiling) { + TStringBuf s; + CityHash64(s); + CityHash64WithSeed(s, 1); + CityHash64WithSeeds(s, 1, 2); + CityHash128(s); + CityHash128WithSeed(s, uint128(1, 2)); + UNIT_ASSERT(s.empty()); + } +} diff --git a/util/digest/fnv.cpp b/util/digest/fnv.cpp new file mode 100644 index 00000000000..57fc5964c31 --- /dev/null +++ b/util/digest/fnv.cpp @@ -0,0 +1 @@ +#include "fnv.h" diff --git a/util/digest/fnv.h b/util/digest/fnv.h new file mode 100644 index 00000000000..87b41a3de7e --- /dev/null +++ b/util/digest/fnv.h @@ -0,0 +1,73 @@ +#pragma once + +#include + +#define FNV32INIT 2166136261U +#define FNV32PRIME 16777619U +#define FNV64INIT ULL(14695981039346656037) +#define FNV64PRIME ULL(1099511628211) + +namespace NFnvPrivate { + template + constexpr ui32 FnvHash32(It b, It e, ui32 init) { + while (b != e) { + init = (init * FNV32PRIME) ^ (unsigned char)*b++; + } + + return init; + } + + template + constexpr ui64 FnvHash64(It b, It e, ui64 init) { + while (b != e) { + init = (init * FNV64PRIME) ^ (unsigned char)*b++; + } + + return init; + } + + template + struct TFnvHelper; + +#define DEF_FNV(t) \ + template <> \ + struct TFnvHelper { \ + static const ui##t Init = FNV##t##INIT; \ + template \ + static constexpr ui##t FnvHash(It b, It e, ui##t init) { \ + return FnvHash##t(b, e, init); \ + } \ + }; + + DEF_FNV(32) + DEF_FNV(64) + +#undef DEF_FNV +} + +template +static constexpr T FnvHash(It b, It e, T init) { + static_assert(sizeof(*b) == 1, "expect sizeof(*b) == 1"); + + return (T)NFnvPrivate::TFnvHelper<8 * sizeof(T)>::FnvHash(b, e, init); +} + +template +static constexpr T FnvHash(It b, It e) { + return FnvHash(b, e, (T)NFnvPrivate::TFnvHelper<8 * sizeof(T)>::Init); +} + +template +static constexpr T FnvHash(const void* buf, size_t len, T init) { + return FnvHash((const unsigned char*)buf, (const unsigned char*)buf + len, init); +} + +template +static constexpr T FnvHash(const void* buf, size_t len) { + return FnvHash((const unsigned char*)buf, (const unsigned char*)buf + len); +} + +template +static constexpr T FnvHash(const Buf& buf) { + return FnvHash(buf.data(), buf.size() * sizeof(*buf.data())); +} diff --git a/util/digest/fnv_ut.cpp b/util/digest/fnv_ut.cpp new file mode 100644 index 00000000000..039e110eaa9 --- /dev/null +++ b/util/digest/fnv_ut.cpp @@ -0,0 +1,23 @@ +#include "fnv.h" + +#include + +Y_UNIT_TEST_SUITE(TFnvTest) { + Y_UNIT_TEST(TestFnv32) { + const auto h32 = ULL(2849763999); + UNIT_ASSERT_EQUAL(FnvHash("1234567", 7), h32); + UNIT_ASSERT_EQUAL(FnvHash(TStringBuf("1234567")), h32); + + UNIT_ASSERT_EQUAL(FnvHash(nullptr, 0), FNV32INIT); + UNIT_ASSERT_EQUAL(FnvHash(TStringBuf()), FNV32INIT); + } + + Y_UNIT_TEST(TestFnv64) { + const auto h64 = ULL(2449551094593701855); + UNIT_ASSERT_EQUAL(FnvHash("1234567", 7), h64); + UNIT_ASSERT_EQUAL(FnvHash(TStringBuf("1234567")), h64); + + UNIT_ASSERT_EQUAL(FnvHash(nullptr, 0), FNV64INIT); + UNIT_ASSERT_EQUAL(FnvHash(TStringBuf()), FNV64INIT); + } +} diff --git a/util/digest/multi.cpp b/util/digest/multi.cpp new file mode 100644 index 00000000000..e4a59f0e674 --- /dev/null +++ b/util/digest/multi.cpp @@ -0,0 +1 @@ +#include "multi.h" diff --git a/util/digest/multi.h b/util/digest/multi.h new file mode 100644 index 00000000000..8e73fbbbe98 --- /dev/null +++ b/util/digest/multi.h @@ -0,0 +1,14 @@ +#pragma once + +#include "numeric.h" +#include + +template +constexpr size_t MultiHash(const TOne& one) noexcept { + return THash()(one); +} + +template +constexpr size_t MultiHash(const THead& head, const TTail&... tail) noexcept { + return CombineHashes(MultiHash(tail...), THash()(head)); +} diff --git a/util/digest/multi.pxd b/util/digest/multi.pxd new file mode 100644 index 00000000000..8b4fae5016c --- /dev/null +++ b/util/digest/multi.pxd @@ -0,0 +1,2 @@ +cdef extern from "" nogil: + size_t MultiHash(...); diff --git a/util/digest/multi_ut.cpp b/util/digest/multi_ut.cpp new file mode 100644 index 00000000000..dff64ff0cc1 --- /dev/null +++ b/util/digest/multi_ut.cpp @@ -0,0 +1,38 @@ +#include "multi.h" + +#include + +#include + +class TMultiHashTest: public TTestBase { + UNIT_TEST_SUITE(TMultiHashTest); + UNIT_TEST(TestStrInt) + UNIT_TEST(TestIntStr) + UNIT_TEST(TestSimpleCollision) + UNIT_TEST(TestTypes) + UNIT_TEST_SUITE_END(); + +private: + inline void TestStrInt() { + UNIT_ASSERT_EQUAL(MultiHash(TString("1234567"), static_cast(123)), static_cast(ULL(17038203285960021630))); + } + + inline void TestIntStr() { + UNIT_ASSERT_EQUAL(MultiHash(static_cast(123), TString("1234567")), static_cast(ULL(9973288649881090712))); + } + + inline void TestSimpleCollision() { + UNIT_ASSERT_UNEQUAL(MultiHash(1, 1, 0), MultiHash(2, 2, 0)); + } + + inline void TestTypes() { + UNIT_ASSERT_EQUAL(MultiHash("aaa", (ui64)123), MultiHash("aaa", 123)); + UNIT_ASSERT_EQUAL(MultiHash("aaa", (i64)123), MultiHash("aaa", 123)); + UNIT_ASSERT_EQUAL(MultiHash("aaa", (i32)123), MultiHash("aaa", 123)); + UNIT_ASSERT_EQUAL(MultiHash("aaa", (ui32)123), MultiHash("aaa", 123)); + UNIT_ASSERT_EQUAL(MultiHash("aaa", (i64)-123), MultiHash("aaa", -123)); + UNIT_ASSERT_EQUAL(MultiHash("aaa", (i32)-123), MultiHash("aaa", -123)); + } +}; + +UNIT_TEST_SUITE_REGISTRATION(TMultiHashTest); diff --git a/util/digest/multi_ut.pyx b/util/digest/multi_ut.pyx new file mode 100644 index 00000000000..f803dca7796 --- /dev/null +++ b/util/digest/multi_ut.pyx @@ -0,0 +1,19 @@ +from util.digest.multi cimport MultiHash +from util.generic.string cimport TString + +import pytest +import unittest + + +class TestMultiHash(unittest.TestCase): + + def test_str_int(self): + value = MultiHash(TString(b"1234567"), 123) + self.assertEqual(value, 17038203285960021630) + + def test_int_str(self): + value = MultiHash(123, TString(b"1234567")) + self.assertEqual(value, 9973288649881090712) + + def test_collision(self): + self.assertNotEqual(MultiHash(1, 1, 0), MultiHash(2, 2, 0)) diff --git a/util/digest/murmur.cpp b/util/digest/murmur.cpp new file mode 100644 index 00000000000..ebaad592203 --- /dev/null +++ b/util/digest/murmur.cpp @@ -0,0 +1,129 @@ +#include "murmur.h" + +#include + +namespace NMurmurPrivate { + //----------------------------------------------------------------------------- + // MurmurHash2, by Austin Appleby + + // Note - This code makes a few assumptions about how your machine behaves - + + // 1. We can read a 4-byte value from any address without crashing + // 2. sizeof(int) == 4 + + // And it has a few limitations - + + // 1. It will not work incrementally. + // 2. It will not produce the same results on little-endian and big-endian + // machines. + + Y_NO_INLINE ui32 MurmurHash32(const void* key, size_t len, ui32 seed) noexcept { + const ui32 m = 0x5bd1e995; + const int r = 24; + ui32 h = ui32(seed ^ len); + + TUnalignedMemoryIterator iter(key, len); + + while (!iter.AtEnd()) { + ui32 k = iter.Next(); + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + } + + const unsigned char* data = iter.Last(); + + switch (iter.Left()) { + case 3: + h ^= data[2] << 16; + [[fallthrough]]; + + case 2: + h ^= data[1] << 8; + [[fallthrough]]; + + case 1: + h ^= data[0]; + h *= m; + break; + } + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; + } + + //----------------------------------------------------------------------------- + // MurmurHash2, 64-bit versions, by Austin Appleby + + // The same caveats as 32-bit MurmurHash2 apply here - beware of alignment + // and endian-ness issues if used across multiple platforms. + + // 64-bit hash for 64-bit platforms + + Y_NO_INLINE ui64 MurmurHash64(const void* key, size_t len, ui64 seed) noexcept { + const ui64 m = ULL(0xc6a4a7935bd1e995); + const int r = 47; + + ui64 h = seed ^ (len * m); + TUnalignedMemoryIterator iter(key, len); + + while (!iter.AtEnd()) { + ui64 k = iter.Next(); + + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + } + + const unsigned char* data2 = iter.Last(); + + switch (iter.Left()) { + case 7: + h ^= ui64(data2[6]) << 48; + [[fallthrough]]; + + case 6: + h ^= ui64(data2[5]) << 40; + [[fallthrough]]; + + case 5: + h ^= ui64(data2[4]) << 32; + [[fallthrough]]; + + case 4: + h ^= ui64(data2[3]) << 24; + [[fallthrough]]; + + case 3: + h ^= ui64(data2[2]) << 16; + [[fallthrough]]; + + case 2: + h ^= ui64(data2[1]) << 8; + [[fallthrough]]; + + case 1: + h ^= ui64(data2[0]); + h *= m; + break; + } + + h ^= h >> r; + h *= m; + h ^= h >> r; + + return h; + } +} + +template size_t MurmurHash(const void* buf, size_t len) noexcept; diff --git a/util/digest/murmur.h b/util/digest/murmur.h new file mode 100644 index 00000000000..a54902006c4 --- /dev/null +++ b/util/digest/murmur.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +/* + * murmur2 from http://murmurhash.googlepages.com/ + * + */ +namespace NMurmurPrivate { + Y_PURE_FUNCTION ui32 MurmurHash32(const void* key, size_t len, ui32 seed) noexcept; + + Y_PURE_FUNCTION ui64 MurmurHash64(const void* key, size_t len, ui64 seed) noexcept; + + template + struct TMurHelper; + +#define DEF_MUR(t) \ + template <> \ + struct TMurHelper { \ + static inline ui##t MurmurHash(const void* buf, size_t len, ui##t init) noexcept { \ + return MurmurHash##t(buf, len, init); \ + } \ + }; + + DEF_MUR(32) + DEF_MUR(64) + +#undef DEF_MUR +} + +template +inline T MurmurHash(const void* buf, size_t len, T init) noexcept { + return (T)NMurmurPrivate::TMurHelper<8 * sizeof(T)>::MurmurHash(buf, len, init); +} + +template +inline T MurmurHash(const void* buf, size_t len) noexcept { + return MurmurHash(buf, len, (T)0); +} + +template +struct TMurmurHash { + TOut operator()(const void* buf, size_t len) const noexcept { + return MurmurHash(buf, len); + } + + template + TOut operator()(const TArrayRef& data) const noexcept { + return operator()(data.data(), data.size() * sizeof(ElementType)); + } +}; diff --git a/util/digest/murmur_ut.cpp b/util/digest/murmur_ut.cpp new file mode 100644 index 00000000000..29287668bc4 --- /dev/null +++ b/util/digest/murmur_ut.cpp @@ -0,0 +1,85 @@ + +#include "murmur.h" + +#include + +class TMurmurHashTest: public TTestBase { + UNIT_TEST_SUITE(TMurmurHashTest); + UNIT_TEST(TestHash32) + UNIT_TEST(TestUnalignedHash32) + UNIT_TEST(TestHash64) + UNIT_TEST(TestUnalignedHash64) + UNIT_TEST(TestWrapperBiggerTypes) + UNIT_TEST_SUITE_END(); + +private: + inline void TestHash32() { + ui8 buf[256]; + + for (size_t i = 0; i < 256; ++i) { + buf[i] = i; + } + + Test(buf, 256, 2373126550UL); + TestWrapper({buf, buf + 256}, 2373126550UL); + Test(buf, 255, 3301607533UL); + Test(buf, 254, 2547410121UL); + Test(buf, 253, 80030810UL); + } + + inline void TestUnalignedHash32() { + ui8 buf[257]; + ui8* unalignedBuf = buf + 1; + + for (size_t i = 0; i < 256; ++i) { + unalignedBuf[i] = i; + } + + Test(unalignedBuf, 256, 2373126550UL); + } + + inline void TestHash64() { + ui8 buf[256]; + + for (size_t i = 0; i < 256; ++i) { + buf[i] = i; + } + + Test(buf, 256, ULL(12604435678857905857)); + TestWrapper({buf, buf + 256}, ULL(12604435678857905857)); + Test(buf, 255, ULL(1708835094528446095)); + Test(buf, 254, ULL(5077937678736514994)); + Test(buf, 253, ULL(11553864555081396353)); + } + + inline void TestUnalignedHash64() { + ui8 buf[257]; + ui8* unalignedBuf = buf + 1; + + for (size_t i = 0; i < 256; ++i) { + unalignedBuf[i] = i; + } + + Test(unalignedBuf, 256, ULL(12604435678857905857)); + } + + inline void TestWrapperBiggerTypes() { + ui32 buf[] = {24, 42}; + TestWrapper({buf, buf + 2}, MurmurHash(buf, sizeof(ui32) * 2)); + TestWrapper({buf, buf + 2}, MurmurHash(buf, sizeof(ui32) * 2)); + } + +private: + template + inline void Test(const void* data, size_t len, T expected) { + UNIT_ASSERT_STRINGS_EQUAL(ToString(MurmurHash(data, len)), ToString(expected)); + } + + template + inline void TestWrapper(const TArrayRef& array, T expected) { + auto val = TMurmurHash()(array); + UNIT_ASSERT_EQUAL(val, expected); + } +}; + +UNIT_TEST_SUITE_REGISTRATION(TMurmurHashTest); diff --git a/util/digest/numeric.cpp b/util/digest/numeric.cpp new file mode 100644 index 00000000000..d4d3f063fa4 --- /dev/null +++ b/util/digest/numeric.cpp @@ -0,0 +1 @@ +#include "numeric.h" diff --git a/util/digest/numeric.h b/util/digest/numeric.h new file mode 100644 index 00000000000..791bce55a96 --- /dev/null +++ b/util/digest/numeric.h @@ -0,0 +1,93 @@ +#pragma once + +#include +#include + +#include + +/* + * original url (now dead): http://www.cris.com/~Ttwang/tech/inthash.htm + * copy: https://gist.github.com/badboy/6267743 + */ + +static constexpr ui8 IntHashImpl(ui8 key8) noexcept { + size_t key = key8; + + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + + return static_cast(key); +} + +static constexpr ui16 IntHashImpl(ui16 key16) noexcept { + size_t key = key16; + + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + + return static_cast(key); +} + +static constexpr ui32 IntHashImpl(ui32 key) noexcept { + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + + return key; +} + +static constexpr ui64 IntHashImpl(ui64 key) noexcept { + key += ~(key << 32); + key ^= (key >> 22); + key += ~(key << 13); + key ^= (key >> 8); + key += (key << 3); + key ^= (key >> 15); + key += ~(key << 27); + key ^= (key >> 31); + + return key; +} + +template +static constexpr T IntHash(T t) noexcept { + using TCvt = TFixedWidthUnsignedInt; + + return IntHashImpl((TCvt)(t)); +} + +/* + * can handle floats && pointers + */ +template +static constexpr size_t NumericHash(T t) noexcept { + if constexpr (std::is_floating_point::value) { + if (t == T(0)) { // the negative zero is equal to the positive zero, but has different bitwise representation + t = T(0); // make sure that the hash will be the same for both kind of zeros + } + } + using TCvt = TFixedWidthUnsignedInt; + + union Y_HIDDEN { + T t; + TCvt cvt; + } u{t}; + + return (size_t)IntHash(u.cvt); +} + +template +[[nodiscard]] static constexpr T CombineHashes(T l, T r) noexcept { + return IntHash(l) ^ r; +} diff --git a/util/digest/sequence.cpp b/util/digest/sequence.cpp new file mode 100644 index 00000000000..5ab731f9bdf --- /dev/null +++ b/util/digest/sequence.cpp @@ -0,0 +1 @@ +#include "sequence.h" diff --git a/util/digest/sequence.h b/util/digest/sequence.h new file mode 100644 index 00000000000..712331007b8 --- /dev/null +++ b/util/digest/sequence.h @@ -0,0 +1,48 @@ +#pragma once + +#include "numeric.h" +#include +#include + +template +class TRangeHash { +private: + template + using TBase = std::conditional_t< + !std::is_void::value, + ElementHash, + THash>; + +public: + template + size_t operator()(const Range& range) const { + size_t accumulated = 0; + for (const auto& element : range) { + accumulated = CombineHashes(accumulated, TBase()(element)); + } + return accumulated; + } +}; + +using TSimpleRangeHash = TRangeHash<>; + +template +class TContiguousHash { +private: + template + using TBase = std::conditional_t< + !std::is_void::value, + RegionHash, + TRangeHash>; + +public: + template + auto operator()(const ContainerType& container) const { + return operator()(MakeArrayRef(container)); + } + + template + auto operator()(const TArrayRef& data) const { + return TBase()(data); + } +}; diff --git a/util/digest/sequence_ut.cpp b/util/digest/sequence_ut.cpp new file mode 100644 index 00000000000..87d6102ee59 --- /dev/null +++ b/util/digest/sequence_ut.cpp @@ -0,0 +1,62 @@ +#include "sequence.h" + +#include +#include +#include + +class TRangeHashTest: public TTestBase { + UNIT_TEST_SUITE(TRangeHashTest); + UNIT_TEST(TestStrokaInt) + UNIT_TEST(TestIntVector) + UNIT_TEST(TestOneElement) + UNIT_TEST(TestMap); + UNIT_TEST(TestCollectionIndependancy); + UNIT_TEST_SUITE_END(); + +private: + inline void TestStrokaInt() { + const size_t canonicalHash = static_cast(ULL(12727184940294366172)); + UNIT_ASSERT_EQUAL(canonicalHash, TRangeHash<>()(TString("12345"))); + } + + inline void TestIntVector() { + const size_t canonicalHash = static_cast(ULL(1351128487744230578)); + TVector testVec = {1, 2, 4, 3}; + UNIT_ASSERT_EQUAL(canonicalHash, TRangeHash<>()(testVec)); + } + + inline void TestOneElement() { + const int testVal = 42; + TVector testVec = {testVal}; + UNIT_ASSERT_UNEQUAL(THash()(testVal), TRangeHash<>()(testVec)); + } + + inline void TestMap() { + const size_t canonicalHash = static_cast(ULL(4415387926488545605)); + TMap testMap{{"foo", 123}, {"bar", 456}}; + UNIT_ASSERT_EQUAL(canonicalHash, TRangeHash<>()(testMap)); + } + + inline void TestCollectionIndependancy() { + TVector testVec = {'a', 'b', 'c'}; + TString testStroka = "abc"; + UNIT_ASSERT_EQUAL(TRangeHash<>()(testVec), TRangeHash<>()(testStroka)); + } +}; + +class TSequenceHashTest: public TTestBase { + UNIT_TEST_SUITE(TSequenceHashTest); + UNIT_TEST(TestSimpleBuffer) + UNIT_TEST_SUITE_END(); + +private: + inline void TestSimpleBuffer() { + int arr[] = {1, 2, 3}; + const size_t canonicalHash = static_cast(ULL(3903918011533391876)); + TContiguousHash hasher; + UNIT_ASSERT_EQUAL(canonicalHash, hasher(TArrayRef(arr, arr + 3))); + } +}; + +UNIT_TEST_SUITE_REGISTRATION(TRangeHashTest); +UNIT_TEST_SUITE_REGISTRATION(TSequenceHashTest); diff --git a/util/digest/ut/ya.make b/util/digest/ut/ya.make new file mode 100644 index 00000000000..d32f5572a83 --- /dev/null +++ b/util/digest/ut/ya.make @@ -0,0 +1,13 @@ +UNITTEST_FOR(util) + +SRCS( + digest/city_ut.cpp + digest/fnv_ut.cpp + digest/multi_ut.cpp + digest/murmur_ut.cpp + digest/sequence_ut.cpp +) + +INCLUDE(${ARCADIA_ROOT}/util/tests/ya_util_tests.inc) + +END() diff --git a/util/digest/ut_cython/test_digest.py b/util/digest/ut_cython/test_digest.py new file mode 100644 index 00000000000..89c95df720d --- /dev/null +++ b/util/digest/ut_cython/test_digest.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +from __future__ import print_function, absolute_import, division + +from util.digest.multi_ut import TestMultiHash + +# Test discovery does not work in cython modules. +# Reexporting test classes here to satisfy pylint and pytest. + +__all__ = [ + 'TestMultiHash', +] diff --git a/util/digest/ut_cython/ya.make b/util/digest/ut_cython/ya.make new file mode 100644 index 00000000000..085df16c4fb --- /dev/null +++ b/util/digest/ut_cython/ya.make @@ -0,0 +1,14 @@ +PY23_TEST() + +SRCDIR(util/digest) + +PY_SRCS( + NAMESPACE util.digest + multi_ut.pyx +) + +TEST_SRCS( + test_digest.py +) + +END() diff --git a/util/digest/ya.make b/util/digest/ya.make new file mode 100644 index 00000000000..3741df77cf5 --- /dev/null +++ b/util/digest/ya.make @@ -0,0 +1,12 @@ +IF (NOT OS_EMSCRIPTEN) + RECURSE( + benchmark + ut + ) +ENDIF() + +IF (NOT OS_IOS AND NOT OS_ANDROID AND NOT OS_EMSCRIPTEN AND NOT USE_SYSTEM_PYTHON) + RECURSE( + ut_cython + ) +ENDIF() diff --git a/util/draft/CMakeLists.txt b/util/draft/CMakeLists.txt new file mode 100644 index 00000000000..5ea1f0bd7dd --- /dev/null +++ b/util/draft/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(util-draft-old) + +target_sources(util-draft-old PRIVATE + ${YDB_SDK_SOURCE_DIR}/util/draft/date.cpp + ${YDB_SDK_SOURCE_DIR}/util/draft/datetime.cpp + ${YDB_SDK_SOURCE_DIR}/util/draft/enum.cpp + ${YDB_SDK_SOURCE_DIR}/util/draft/holder_vector.cpp + ${YDB_SDK_SOURCE_DIR}/util/draft/ip.cpp + ${YDB_SDK_SOURCE_DIR}/util/draft/matrix.cpp + ${YDB_SDK_SOURCE_DIR}/util/draft/memory.cpp +) diff --git a/util/draft/date.cpp b/util/draft/date.cpp new file mode 100644 index 00000000000..a290c460505 --- /dev/null +++ b/util/draft/date.cpp @@ -0,0 +1,113 @@ +#include "date.h" + +#include +#include +#include + +time_t GetDateStart(time_t ts) { + tm dateTm; + memset(&dateTm, 0, sizeof(tm)); + localtime_r(&ts, &dateTm); + + dateTm.tm_isdst = -1; + + dateTm.tm_sec = 0; + dateTm.tm_min = 0; + dateTm.tm_hour = 0; + return mktime(&dateTm); +} + +static time_t ParseDate(const char* date, const char* format) { + tm dateTm; + memset(&dateTm, 0, sizeof(tm)); + if (!strptime(date, format, &dateTm)) { + ythrow yexception() << "Invalid date string and format: " << date << ", " << format; + } + return mktime(&dateTm); +} + +static time_t ParseDate(const char* dateStr) { + if (strlen(dateStr) != 8) { + ythrow yexception() << "Invalid date string: " << dateStr; + } + + return ParseDate(dateStr, "%Y%m%d"); +} + +template <> +TDate FromStringImpl(const char* data, size_t len) { + return TDate(ParseDate(TString(data, len).data())); +} + +TDate::TDate(const char* yyyymmdd) + : Timestamp(GetDateStart(ParseDate(yyyymmdd))) +{ +} + +TDate::TDate(const TString& yyyymmdd) + : Timestamp(GetDateStart(ParseDate(yyyymmdd.c_str()))) +{ +} + +TDate::TDate(time_t ts) + : Timestamp(GetDateStart(ts)) +{ +} + +TDate::TDate(const TString& date, const TString& format) + : Timestamp(GetDateStart(ParseDate(date.data(), format.data()))) +{ +} + +TDate::TDate(unsigned year, unsigned month, unsigned monthDay) { + tm dateTm; + Zero(dateTm); + dateTm.tm_year = year - 1900; + dateTm.tm_mon = month - 1; + dateTm.tm_mday = monthDay; + dateTm.tm_isdst = -1; + Timestamp = mktime(&dateTm); + if (Timestamp == (time_t)-1) { + ythrow yexception() << "Invalid TDate args:(" << year << ',' << month << ',' << monthDay << ')'; + } +} + +time_t TDate::GetStartUTC() const { + tm dateTm; + localtime_r(&Timestamp, &dateTm); + dateTm.tm_isdst = -1; + dateTm.tm_sec = 0; + dateTm.tm_min = 0; + dateTm.tm_hour = 0; + return TimeGM(&dateTm); +} + +TString TDate::ToStroka(const char* format) const { + tm dateTm; + localtime_r(&Timestamp, &dateTm); + return Strftime(format, &dateTm); +} + +unsigned TDate::GetWeekDay() const { + tm dateTm; + localtime_r(&Timestamp, &dateTm); + return (unsigned)dateTm.tm_wday; +} + +unsigned TDate::GetYear() const { + tm dateTm; + localtime_r(&Timestamp, &dateTm); + return ((unsigned)dateTm.tm_year) + 1900; +} + +unsigned TDate::GetMonth() const { + tm dateTm; + localtime_r(&Timestamp, &dateTm); + return ((unsigned)dateTm.tm_mon) + 1; +} + +unsigned TDate::GetMonthDay() const { + tm dateTm; + localtime_r(&Timestamp, &dateTm); + return (unsigned)dateTm.tm_mday; +} diff --git a/util/draft/date.h b/util/draft/date.h new file mode 100644 index 00000000000..e3eb616fe5d --- /dev/null +++ b/util/draft/date.h @@ -0,0 +1,129 @@ +#pragma once + +#include +#include +#include +#include + +#include + +// XXX: uses system calls for trivial things. may be very slow therefore. + +time_t GetDateStart(time_t ts); + +// Local date (without time zone) +class TDate { + // XXX: wrong: must store number of days since epoch + time_t Timestamp; + +public: + TDate() + : Timestamp(0) + { + } + + // XXX: wrong. Should be replace with two methods: TodayGmt() and TodayLocal() + static TDate Today() { + return TDate(time(nullptr)); + } + + TDate(const char* yyyymmdd); + TDate(const TString& yyyymmdd); + TDate(unsigned year, unsigned month, unsigned monthDay); // month from 01, monthDay from 01 + TDate(const TString& date, const TString& format); + + explicit TDate(time_t t); + + time_t GetStart() const { + return Timestamp; + } + + time_t GetStartUTC() const; + + TString ToStroka(const char* format = "%Y%m%d") const; + + TDate& operator++() { + Timestamp = GetDateStart(Timestamp + 3 * (SECONDS_IN_DAY / 2)); + return *this; + } + + TDate& operator--() { + Timestamp = GetDateStart(Timestamp - SECONDS_IN_DAY / 2); + return *this; + } + + TDate& operator+=(unsigned days) { + Timestamp = GetDateStart(Timestamp + days * SECONDS_IN_DAY + SECONDS_IN_DAY / 2); + return *this; + } + + TDate& operator-=(unsigned days) { + Timestamp = GetDateStart(Timestamp - days * SECONDS_IN_DAY + SECONDS_IN_DAY / 2); + return *this; + } + + TDate operator+(unsigned days) const { + return TDate(Timestamp + days * SECONDS_IN_DAY + SECONDS_IN_DAY / 2); + } + + TDate operator-(unsigned days) const { + return TDate(Timestamp - days * SECONDS_IN_DAY + SECONDS_IN_DAY / 2); + } + + unsigned GetWeekDay() const; // days since Sunday + + unsigned GetYear() const; + unsigned GetMonth() const; // from 01 + unsigned GetMonthDay() const; // from 01 + + friend bool operator<(const TDate& left, const TDate& right); + friend bool operator>(const TDate& left, const TDate& right); + friend bool operator<=(const TDate& left, const TDate& right); + friend bool operator>=(const TDate& left, const TDate& right); + friend bool operator==(const TDate& left, const TDate& right); + friend int operator-(const TDate& left, const TDate& right); + + friend IInputStream& operator>>(IInputStream& left, TDate& right); + friend IOutputStream& operator<<(IOutputStream& left, const TDate& right); +}; + +Y_DECLARE_PODTYPE(TDate); + +inline bool operator<(const TDate& left, const TDate& right) { + return left.Timestamp < right.Timestamp; +} + +inline bool operator>(const TDate& left, const TDate& right) { + return left.Timestamp > right.Timestamp; +} + +inline bool operator<=(const TDate& left, const TDate& right) { + return left.Timestamp <= right.Timestamp; +} + +inline bool operator>=(const TDate& left, const TDate& right) { + return left.Timestamp >= right.Timestamp; +} + +inline bool operator==(const TDate& left, const TDate& right) { + return left.Timestamp == right.Timestamp; +} + +inline int operator-(const TDate& left, const TDate& right) { + if (left < right) { + return -(right - left); + } + return static_cast((left.Timestamp + SECONDS_IN_DAY / 2 - right.Timestamp) / SECONDS_IN_DAY); +} + +inline IInputStream& operator>>(IInputStream& left, TDate& right) { + TString stroka; + left >> stroka; + TDate date(stroka.c_str()); + right = date; + return left; +} + +inline IOutputStream& operator<<(IOutputStream& left, const TDate& right) { + return left << right.ToStroka(); +} diff --git a/util/draft/date_ut.cpp b/util/draft/date_ut.cpp new file mode 100644 index 00000000000..8c33a6c1cfd --- /dev/null +++ b/util/draft/date_ut.cpp @@ -0,0 +1,36 @@ +#include "date.h" + +#include + +Y_UNIT_TEST_SUITE(TDateTest) { + Y_UNIT_TEST(ComponentsTest) { + { + TDate d("20110215"); + UNIT_ASSERT_EQUAL(d.GetYear(), 2011); + UNIT_ASSERT_EQUAL(d.GetMonth(), 2); + UNIT_ASSERT_EQUAL(d.GetMonthDay(), 15); + UNIT_ASSERT_EQUAL(d.ToStroka("%Y%m%d"), "20110215"); + UNIT_ASSERT_EQUAL(d.ToStroka(), "20110215"); + UNIT_ASSERT_EQUAL(d.ToStroka("%Y--%m--%d"), "2011--02--15"); + UNIT_ASSERT_EQUAL(d.ToStroka("%U"), "07"); + UNIT_ASSERT_EQUAL(d.GetStartUTC(), 1297728000); + } + { + TDate d(2005, 6, 3); + UNIT_ASSERT_EQUAL(d.GetYear(), 2005); + UNIT_ASSERT_EQUAL(d.GetMonth(), 6); + UNIT_ASSERT_EQUAL(d.GetMonthDay(), 3); + UNIT_ASSERT_EQUAL(d.ToStroka(), "20050603"); + UNIT_ASSERT_EQUAL(d.ToStroka("____%Y__%m____%d"), "____2005__06____03"); + UNIT_ASSERT_EQUAL(d.GetStartUTC(), 1117756800); + } + { + TDate d("2011-02-15", "%Y-%m-%d"); + UNIT_ASSERT_EQUAL(d.GetYear(), 2011); + UNIT_ASSERT_EQUAL(d.GetMonth(), 2); + UNIT_ASSERT_EQUAL(d.GetMonthDay(), 15); + UNIT_ASSERT_EQUAL(d.ToStroka("%Y%m%d"), "20110215"); + UNIT_ASSERT_EQUAL(d.GetStartUTC(), 1297728000); + } + } +} diff --git a/util/draft/datetime.cpp b/util/draft/datetime.cpp new file mode 100644 index 00000000000..d290021d5e2 --- /dev/null +++ b/util/draft/datetime.cpp @@ -0,0 +1,236 @@ +#include "datetime.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace NDatetime { + const ui32 MonthDays[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, //nleap + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} //leap + }; + + const ui32 MonthDaysNewYear[2][13] = { + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, //nleap + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} //leap + }; + + void YDayToMonthAndDay(ui32 yday, bool isleap, ui32* month, ui32* mday) { + const ui32* begin = MonthDaysNewYear[isleap] + 1; + const ui32* end = begin + 12; + // [31, ..., 365] or [31, ..., 366] (12 elements) + + const ui32* pos = UpperBound(begin, end, yday); + Y_ENSURE(pos != end, "day no. " << yday << " does not exist in " << (isleap ? "leap" : "non-leap") << " year"); + + *month = pos - begin; + *mday = yday - *(pos - 1) + 1; + + Y_ASSERT((*month < 12) && (1 <= *mday) && (*mday <= MonthDays[isleap][*month])); + } + + struct TTimeData { + i32 IsDst = 0; + i32 GMTOff = 0; + + TTimeData(time_t t) { + struct ::tm tt; + ::localtime_r(&t, &tt); +#ifndef _win_ + GMTOff = tt.tm_gmtoff; +#else + TIME_ZONE_INFORMATION tz; + switch (GetTimeZoneInformation(&tz)) { + case TIME_ZONE_ID_UNKNOWN: + GMTOff = tz.Bias * -60; + break; + case TIME_ZONE_ID_STANDARD: + GMTOff = (tz.Bias + tz.StandardBias) * -60; + break; + case TIME_ZONE_ID_DAYLIGHT: + GMTOff = (tz.Bias + tz.DaylightBias) * -60; + break; + default: + break; + } +#endif + IsDst = tt.tm_isdst; + } + }; + + TSimpleTM TSimpleTM::CurrentUTC() { + return New((time_t)TInstant::MicroSeconds(InterpolatedMicroSeconds()).Seconds()); + } + + TSimpleTM TSimpleTM::New(time_t t, i32 gmtoff, i8 isdst) { + time_t tt = t + gmtoff + isdst * 3600; + struct tm tmSys; + Zero(tmSys); + GmTimeR(&tt, &tmSys); + tmSys.tm_isdst = isdst; +#ifndef _win_ + tmSys.tm_gmtoff = gmtoff; +#endif + + return New(tmSys); + } + + TSimpleTM TSimpleTM::NewLocal(time_t t) { + TTimeData d(t); + return New(t, d.GMTOff, d.IsDst); + } + + TSimpleTM TSimpleTM::New(const struct tm& t) { + TSimpleTM res; + res.IsDst = t.tm_isdst; + res.Sec = t.tm_sec; + res.Min = t.tm_min; + res.Hour = t.tm_hour; + res.WDay = t.tm_wday; + res.Mon = t.tm_mon; + res.MDay = t.tm_mday; + res.Year = t.tm_year; + res.YDay = t.tm_yday; + res.IsLeap = LeapYearAD(res.Year + 1900); +#ifndef _win_ + res.GMTOff = t.tm_gmtoff; +#endif + return res; + } + + TSimpleTM& TSimpleTM::SetRealDate(ui32 year, ui32 mon, ui32 mday, ui32 hour, ui32 min, ui32 sec, i32 isdst) { + mday = ::Max(mday, 1); + mon = ::Min(::Max(mon, 1), 12); + year = ::Max(year, 1900); + + IsLeap = LeapYearAD(year); + Year = year - 1900; + Mon = mon - 1; + MDay = ::Min(mday, MonthDays[IsLeap][Mon]); + Hour = Max() == hour ? Hour : ::Min(hour, 23); + Min = Max() == min ? Min : ::Min(min, 59); + Sec = Max() == sec ? Sec : ::Min(sec, 60); + IsDst = isdst; + + return RegenerateFields(); + } + + TSimpleTM& TSimpleTM::RegenerateFields() { + return *this = New(AsTimeT(), GMTOff, IsDst); + } + + TSimpleTM& TSimpleTM::Add(EField f, i32 amount) { + if (!amount) { + return *this; + } + + switch (f) { + default: + return *this; + case F_DAY: + amount *= 24; + [[fallthrough]]; + case F_HOUR: + amount *= 60; + [[fallthrough]]; + case F_MIN: + amount *= 60; + [[fallthrough]]; + case F_SEC: { + return *this = New(AsTimeT() + amount, GMTOff, IsDst); + } + case F_YEAR: { + i32 y = amount + (i32)Year; + y = ::Min(Max(y, 0), 255 /*max year*/); + + // YDay may correspond to different MDay if it's March or greater and the years have different leap status + if (Mon > 1) { + YDay += (i32)LeapYearAD(RealYear()) - (i32)LeapYearAD(RealYear()); + } + + Year = y; + IsLeap = LeapYearAD(RealYear()); + return RegenerateFields(); + } + case F_MON: { + i32 m = amount + Mon; + i32 y = (m < 0 ? (-12 + m) : m) / 12; + m = m - y * 12; + + if (y) { + Add(F_YEAR, y); + } + + if (m >= 0 && m < 12) { + MDay = ::Min(MonthDays[IsLeap][m], MDay); + Mon = m; + } + + return RegenerateFields(); + } + } + } + + TString TSimpleTM::ToString(const char* fmt) const { + struct tm t = *this; + return Strftime(fmt, &t); + } + + time_t TSimpleTM::AsTimeT() const { + struct tm t = AsStructTmLocal(); + return TimeGM(&t) - GMTOff - IsDst * 3600; + } + + struct tm TSimpleTM::AsStructTmUTC() const { + struct tm res; + Zero(res); + time_t t = AsTimeT(); + return *GmTimeR(&t, &res); + } + + struct tm TSimpleTM::AsStructTmLocal() const { + struct tm t; + Zero(t); + t.tm_isdst = IsDst; + t.tm_sec = Sec; + t.tm_min = Min; + t.tm_hour = Hour; + t.tm_wday = WDay; + t.tm_mon = Mon; + t.tm_mday = MDay; + t.tm_year = Year; + t.tm_yday = YDay; +#ifndef _win_ + t.tm_gmtoff = GMTOff; +#endif + return t; + } +} + +template <> +void In(IInputStream& in, TMonth& t) { + char buf[4]; + LoadPodArray(&in, buf, 4); + t.Year = FromString(buf, 4); + LoadPodArray(&in, buf, 2); + t.Month = ui8(FromString(buf, 2)) - 1; +} + +template <> +void Out(IOutputStream& o, const TMonth& t) { + o << t.Year << Sprintf("%.2hu", (ui16)(t.Month + 1)); +} + +template <> +TMonth FromStringImpl(const char* s, size_t len) { + TMonth res; + TMemoryInput in(s, len); + in >> res; + return res; +} diff --git a/util/draft/datetime.h b/util/draft/datetime.h new file mode 100644 index 00000000000..7301e32e501 --- /dev/null +++ b/util/draft/datetime.h @@ -0,0 +1,184 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include + +namespace NDatetime { + extern const ui32 MonthDays[2][12]; // !leapYear; !!leapYear + extern const ui32 MonthDaysNewYear[2][13]; // !leapYear; !!leapYear + + inline ui32 YearDaysAD(ui32 year) { + year = Max(year, 1) - 1; //1 AD comes straight after 1 BC, no 0 AD + return year * 365 + year / 4 - year / 100 + year / 400; + } + + inline bool LeapYearAD(ui32 year) { + return (!(year % 4) && (year % 100)) || !(year % 400); + } + + inline ui32 YDayFromMonthAndDay(ui32 month /*0 - based*/, ui32 mday /*1 - based*/, bool isleap) { + return MonthDaysNewYear[isleap][Min(month, (ui32)11u)] + mday - 1; + } + + void YDayToMonthAndDay(ui32 yday /*0 - based*/, bool isleap, ui32* month /*0 - based*/, ui32* mday /*1 - based*/); + + struct TSimpleTM { + enum EField { + F_NONE = 0, + F_SEC, + F_MIN, + F_HOUR, + F_DAY, + F_MON, + F_YEAR + }; + + i32 GMTOff = 0; // -43200 - 50400 seconds + ui16 Year = 0; // from 1900 + ui16 YDay = 0; // 0-365 + ui8 Mon = 0; // 0-11 + ui8 MDay = 0; // 1-31 + ui8 WDay = 0; // 0-6 + ui8 Hour = 0; // 0-23 + ui8 Min = 0; // 0-59 + ui8 Sec = 0; // 0-60 - doesn't care for leap seconds. Most of the time it's ok. + i8 IsDst = 0; // -1/0/1 + bool IsLeap = false; + + public: + static TSimpleTM New(time_t t = 0, i32 gmtoff = 0, i8 isdst = 0); + static TSimpleTM NewLocal(time_t = 0); + + static TSimpleTM New(const struct tm&); + + static TSimpleTM CurrentUTC(); + + TSimpleTM() = default; + + TSimpleTM(ui32 year, ui32 mon, ui32 day, ui32 h = 0, ui32 m = 0, ui32 s = 0) { + Zero(*this); + SetRealDate(year, mon, day, h, m, s); + } + + // keeps the object consistent + TSimpleTM& Add(EField f, i32 amount = 1); + + TString ToString(const char* fmt = "%a, %d %b %Y %H:%M:%S %z") const; + + TSimpleTM& ToUTC() { + return *this = New(AsTimeT()); + } + + bool IsUTC() const { + return !IsDst && !GMTOff; + } + + time_t AsTimeT() const; + + operator time_t() const { + return AsTimeT(); + } + + struct tm AsStructTmLocal() const; + + struct tm AsStructTmUTC() const; + + operator struct tm() const { + return AsStructTmLocal(); + } + + ui32 RealYear() const { + return ui32(Year + 1900); + } + + ui32 RealMonth() const { + return ui32(Mon + 1); + } + + TSimpleTM& SetRealDate(ui32 year, ui32 mon, ui32 mday, ui32 hour = -1, ui32 min = -1, ui32 sec = -1, i32 isdst = 0); + + // regenerates all fields from Year, MDay, Hour, Min, Sec, IsDst, GMTOffset + TSimpleTM& RegenerateFields(); + + friend bool operator==(const TSimpleTM& a, const TSimpleTM& b) { + return a.AsTimeT() == b.AsTimeT(); + } + + friend bool operator==(const TSimpleTM& s, const struct tm& t) { + return s == New(t); + } + + friend bool operator==(const struct tm& t, const TSimpleTM& s) { + return s == t; + } + + friend bool operator!=(const TSimpleTM& a, const TSimpleTM& b) { + return !(a == b); + } + + friend bool operator!=(const TSimpleTM& s, const struct tm& t) { + return !(s == t); + } + + friend bool operator!=(const struct tm& t, const TSimpleTM& s) { + return s != t; + } + }; +} + +inline TString date2str(const time_t date) { + struct tm dateTm; + memset(&dateTm, 0, sizeof(dateTm)); + localtime_r(&date, &dateTm); + char buf[9]; + strftime(buf, sizeof(buf), "%Y%m%d", &dateTm); + return TString(buf); +} + +inline time_t str2date(const TString& dateStr) { + struct tm dateTm; + memset(&dateTm, 0, sizeof(tm)); + strptime(dateStr.data(), "%Y%m%d", &dateTm); + return mktime(&dateTm); +} + +// checks whether time2 > time1 and close enough to it +inline bool AreTimesSeqAndClose(time_t time1, time_t time2, time_t closeInterval = 10) { + return (time2 - time1) <= closeInterval; +} + +// checks whether time2 and time1 are close enough +inline bool AreTimesClose(time_t time1, time_t time2, time_t closeInterval = 10) { + return std::abs(time2 - time1) <= closeInterval; +} + +//////////////////////////////// + +struct TMonth { + ui16 Year; + ui8 Month; + + TMonth(ui16 year = 0, ui8 month = 0) + : Year(year) + , Month(month) + { + } + + TMonth operator-(ui16 n) { + if (n <= Month) { + return TMonth(Year, Month - (ui8)n); + } else { + n -= Month; + return (n % 12) ? TMonth(Year - 1 - (n / 12), 12 - (n % 12)) : TMonth(Year - (n / 12), 0); + } + } +}; + +Y_DECLARE_PODTYPE(NDatetime::TSimpleTM); diff --git a/util/draft/datetime_ut.cpp b/util/draft/datetime_ut.cpp new file mode 100644 index 00000000000..a5e065ef6e8 --- /dev/null +++ b/util/draft/datetime_ut.cpp @@ -0,0 +1,231 @@ +#include "datetime.h" + +#include + +#include + +Y_UNIT_TEST_SUITE(TSimpleTMTest) { + TString PrintMarker(const TString& test, int line) { + return TStringBuilder() << "test " << test << " at line " << line; + } + + TString JoinMarker(const TString& marker, time_t t) { + return TStringBuilder() << marker << " (tstamp=" << t << ")"; + } + + TString PrintMarker(const TString& test, int line, time_t t) { + return JoinMarker(PrintMarker(test, line), t); + } + + void AssertStructTmEqual(const TString& marker, const struct tm& tmt, const NDatetime::TSimpleTM& tms) { + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Sec, tmt.tm_sec, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Min, tmt.tm_min, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Hour, tmt.tm_hour, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.WDay, tmt.tm_wday, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.MDay, tmt.tm_mday, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Mon, tmt.tm_mon, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.YDay, tmt.tm_yday, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Year, tmt.tm_year, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.IsDst, tmt.tm_isdst, marker); +#ifndef _win_ + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.GMTOff, tmt.tm_gmtoff, marker); +#endif + } + + void AssertSimpleTM(const TString& mark, + const NDatetime::TSimpleTM& tms, + time_t tstamp, ui32 year, ui32 mon, ui32 mday, ui32 hour, ui32 minu, ui32 sec) { + TString marker = JoinMarker(mark, tstamp); + struct tm tmt; + Zero(tmt); + GmTimeR(&tstamp, &tmt); + AssertStructTmEqual(marker, tmt, tms); + tmt = tms.AsStructTmUTC(); + time_t tstamp1 = TimeGM(&tmt); + UNIT_ASSERT_VALUES_EQUAL_C(tstamp, tstamp1, marker); + UNIT_ASSERT_VALUES_EQUAL_C(tstamp, tms.AsTimeT(), marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.RealYear(), year, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.RealMonth(), mon, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.MDay, mday, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Hour, hour, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Min, minu, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Sec, sec, marker); + } + + Y_UNIT_TEST(TestLeap) { + using namespace NDatetime; + UNIT_ASSERT(LeapYearAD(2000)); + UNIT_ASSERT(LeapYearAD(2012)); + UNIT_ASSERT(!LeapYearAD(1999)); + UNIT_ASSERT(LeapYearAD(2004)); + UNIT_ASSERT(!LeapYearAD(1900)); + } + + Y_UNIT_TEST(TestYDayConversion) { + using namespace NDatetime; + ui32 month; + ui32 mday; + + for (ui32 yday = 0; yday < 365; ++yday) { + YDayToMonthAndDay(yday, false, &month, &mday); + UNIT_ASSERT_VALUES_EQUAL(yday, YDayFromMonthAndDay(month, mday, false)); + } + for (ui32 yday = 0; yday < 366; ++yday) { + YDayToMonthAndDay(yday, true, &month, &mday); + UNIT_ASSERT_VALUES_EQUAL(yday, YDayFromMonthAndDay(month, mday, true)); + } + + UNIT_ASSERT_EXCEPTION(YDayToMonthAndDay(365, false, &month, &mday), yexception); + UNIT_ASSERT_EXCEPTION(YDayToMonthAndDay(366, true, &month, &mday), yexception); + } + + Y_UNIT_TEST(SimpleTMTest) { + using namespace NDatetime; + + tzset(); + + TSimpleTM::New(-1); //should not die here + + UNIT_ASSERT_VALUES_EQUAL((ui32)0, (ui32)TSimpleTM::New(0)); + UNIT_ASSERT((ui32)TSimpleTM::New(0).IsUTC()); + time_t t = time(nullptr); + + { + struct tm tmt; + Zero(tmt); + gmtime_r(&t, &tmt); + UNIT_ASSERT_VALUES_EQUAL_C((i64)t, (i64)TSimpleTM::New(t).AsTimeT(), ToString(t)); // time_t -> gmt tm -> time_t + UNIT_ASSERT_VALUES_EQUAL_C((i64)t, (i64)TSimpleTM::New(tmt).AsTimeT(), ToString(t)); // gmt tm -> time_t + AssertStructTmEqual(PrintMarker("UTC:time_t", __LINE__, t), + tmt, TSimpleTM::New(t)); + AssertStructTmEqual(PrintMarker("UTC:tm", __LINE__, t), + tmt, TSimpleTM::New(tmt)); + UNIT_ASSERT(TSimpleTM::New(t).IsUTC()); + UNIT_ASSERT(TSimpleTM::New(tmt).IsUTC()); + } + + { + struct tm tmt; + Zero(tmt); + localtime_r(&t, &tmt); + + UNIT_ASSERT_VALUES_EQUAL((i64)t, (i64)TSimpleTM::NewLocal(t).AsTimeT()); // time_t -> local tm -> time_t + UNIT_ASSERT_VALUES_EQUAL((i64)t, (i64)TSimpleTM::New(tmt).AsTimeT()); + AssertStructTmEqual(PrintMarker("local:time_t", __LINE__, t), + tmt, TSimpleTM::NewLocal(t)); + AssertStructTmEqual(PrintMarker("local:tm", __LINE__, t), + tmt, TSimpleTM::New(tmt)); + AssertStructTmEqual(PrintMarker("local:tm:RegenerateFields", __LINE__, t), + tmt, TSimpleTM::New(tmt).RegenerateFields()); + AssertStructTmEqual(PrintMarker("local:time_t:SetRealDate", __LINE__, t), + tmt, TSimpleTM::NewLocal(t).SetRealDate(tmt.tm_year + 1900, tmt.tm_mon + 1, tmt.tm_mday, tmt.tm_hour, tmt.tm_min, tmt.tm_sec, tmt.tm_isdst)); + } + + { + TSimpleTM tt = TSimpleTM::New(0); + + tt.SetRealDate(2012, 3, 30, 5, 6, 7); + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1333083967, 2012, 3, 30, 5, 6, 7); + + tt.SetRealDate(2012, 3, 8, 5, 6, 7); + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1331183167, 2012, 3, 8, 5, 6, 7); + + tt.SetRealDate(2010, 10, 4, 5, 6, 7); + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1286168767, 2010, 10, 4, 5, 6, 7); + + tt.Add(TSimpleTM::F_MON); + AssertSimpleTM(PrintMarker("UTC:AddMonth", __LINE__), + tt, 1288847167, 2010, 11, 4, 5, 6, 7); + + tt.Add(TSimpleTM::F_DAY); + AssertSimpleTM(PrintMarker("UTC:AddDay", __LINE__), + tt, 1288933567, 2010, 11, 5, 5, 6, 7); + + tt.Add(TSimpleTM::F_YEAR); + AssertSimpleTM(PrintMarker("UTC:AddYear", __LINE__), + tt, 1320469567, 2011, 11, 5, 5, 6, 7); + + for (ui32 i = 0; i < 365; ++i) { + tt.Add(TSimpleTM::F_DAY); + } + + AssertSimpleTM(PrintMarker("UTC:365*AddDay", __LINE__), + tt, 1352005567, 2012, 11, 4, 5, 6, 7); + + tt.Add(TSimpleTM::F_MON, -10); + AssertSimpleTM(PrintMarker("UTC:AddMonth(-10)", __LINE__), + tt, 1325653567, 2012, 1, 4, 5, 6, 7); + + tt.Add(TSimpleTM::F_HOUR, -24 * 4 - 6); + AssertSimpleTM(PrintMarker("UTC:AddHour(-102)", __LINE__), + tt, 1325286367, 2011, 12, 30, 23, 6, 7); + } + + { + TSimpleTM tt = TSimpleTM::New(); + + tt.SetRealDate(2012, 2, 29); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1330473600, 2012, 2, 29, 0, 0, 0); + + tt.SetRealDate(2012, 2, 29); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1330473600, 2012, 2, 29, 0, 0, 0); + + tt.SetRealDate(2013, 12, 28); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1388188800, 2013, 12, 28, 0, 0, 0); + + tt.SetRealDate(2012, 10, 23); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1350950400, 2012, 10, 23, 0, 0, 0); + + tt.SetRealDate(2013, 3, 16); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1363392000, 2013, 3, 16, 0, 0, 0); + + tt.SetRealDate(2013, 2, 17); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1361059200, 2013, 2, 17, 0, 0, 0); + + tt.SetRealDate(2012, 12, 23); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1356220800, 2012, 12, 23, 0, 0, 0); + + tt.SetRealDate(2012, 5, 17); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1337212800, 2012, 5, 17, 0, 0, 0); + + tt.SetRealDate(2012, 6, 15); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1339718400, 2012, 6, 15, 0, 0, 0); + + tt.SetRealDate(2009, 3, 17); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1237248000, 2009, 3, 17, 0, 0, 0); + + tt.SetRealDate(2013, 8, 12); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1376265600, 2013, 8, 12, 0, 0, 0); + + tt.SetRealDate(2015, 12, 11, 10, 9, 8); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1449828548, 2015, 12, 11, 10, 9, 8); + } + } +} diff --git a/util/draft/enum.cpp b/util/draft/enum.cpp new file mode 100644 index 00000000000..40c79fb679a --- /dev/null +++ b/util/draft/enum.cpp @@ -0,0 +1 @@ +#include "enum.h" diff --git a/util/draft/enum.h b/util/draft/enum.h new file mode 100644 index 00000000000..18002b7df20 --- /dev/null +++ b/util/draft/enum.h @@ -0,0 +1,136 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +class TEnumNotFoundException: public yexception { +}; + +#define EnumFromString(key, entries) EnumFromStringImpl(key, entries, Y_ARRAY_SIZE(entries)) +#define EnumFromStringWithSize(key, entries, size) EnumFromStringImpl(key, entries, size) +#define FindEnumFromString(key, entries) FindEnumFromStringImpl(key, entries, Y_ARRAY_SIZE(entries)) +#define FindEnumFromStringWithSize(key, entries, size) FindEnumFromStringImpl(key, entries, size) +#define EnumToString(key, entries) EnumToStringImpl(key, entries, Y_ARRAY_SIZE(entries)) +#define EnumToStringWithSize(key, entries, size) EnumToStringImpl(key, entries, size) +#define PrintEnumItems(entries) PrintEnumItemsImpl(entries, Y_ARRAY_SIZE(entries)) + +template +const V* FindEnumFromStringImpl(K1 key, const std::pair* entries, size_t arraySize) { + for (size_t i = 0; i < arraySize; i++) + if (entries[i].first == key) + return &entries[i].second; + return nullptr; +} + +// special version for const char* +template +const V* FindEnumFromStringImpl(const char* key, const std::pair* entries, size_t arraySize) { + for (size_t i = 0; i < arraySize; i++) + if (entries[i].first && key && !strcmp(entries[i].first, key)) + return &entries[i].second; + return nullptr; +} + +template +TString PrintEnumItemsImpl(const std::pair* entries, size_t arraySize) { + TString result; + TStringOutput out(result); + for (size_t i = 0; i < arraySize; i++) + out << (i ? ", " : "") << "'" << entries[i].first << "'"; + return result; +} + +// special version for const char* +template +TString PrintEnumItemsImpl(const std::pair* entries, size_t arraySize) { + TString result; + TStringOutput out(result); + for (size_t i = 0; i < arraySize; i++) + out << (i ? ", " : "") << "'" << (entries[i].first ? entries[i].first : "") << "'"; + return result; +} + +template +const V* EnumFromStringImpl(K1 key, const std::pair* entries, size_t arraySize) { + const V* res = FindEnumFromStringImpl(key, entries, arraySize); + if (res) + return res; + + ythrow TEnumNotFoundException() << "Key '" << key << "' not found in enum. Valid options are: " << PrintEnumItemsImpl(entries, arraySize) << ". "; +} + +template +const K* EnumToStringImpl(V value, const std::pair* entries, size_t arraySize) { + for (size_t i = 0; i < arraySize; i++) + if (entries[i].second == value) + return &entries[i].first; + + TEnumNotFoundException exc; + exc << "Value '" << int(value) << "' not found in enum. Valid values are: "; + for (size_t i = 0; i < arraySize; i++) + exc << (i ? ", " : "") << int(entries[i].second); + exc << ". "; + ythrow exc; +} + +/////////////////////////////////// + +template +inline void SetEnumFlagsForEmptySpec(B& flags, bool allIfEmpty) { + if (allIfEmpty) { + flags.set(); + } else { + flags.reset(); + } +} + +// all set by default +template +inline void SetEnumFlags(const std::pair (&str2Enum)[N], TStringBuf optSpec, + std::bitset& flags, bool allIfEmpty = true) { + if (optSpec.empty()) { + SetEnumFlagsForEmptySpec(flags, allIfEmpty); + } else { + flags.reset(); + for (const auto& it : StringSplitter(optSpec).Split(',')) { + E e = *EnumFromStringImpl(ToString(it.Token()).data(), str2Enum, N); + flags.set(e); + } + } +} + +template +inline void SetEnumFlags(const std::pair* str2Enum, TStringBuf optSpec, + std::bitset& flags, const size_t size, + bool allIfEmpty = true) { + if (optSpec.empty()) { + SetEnumFlagsForEmptySpec(flags, allIfEmpty); + } else { + flags.reset(); + for (const auto& it : StringSplitter(optSpec).Split(',')) { + E e = *EnumFromStringImpl(ToString(it.Token()).data(), str2Enum, size); + flags.set(e); + } + } +} + +// for enums generated with GENERATE_ENUM_SERIALIZATION +template +inline void SetEnumFlags(TStringBuf optSpec, std::bitset& flags, bool allIfEmpty = true) { + if (optSpec.empty()) { + SetEnumFlagsForEmptySpec(flags, allIfEmpty); + } else { + flags.reset(); + for (const auto& it : StringSplitter(optSpec).Split(',')) { + E e; + if (!TryFromString(it.Token(), e)) + ythrow yexception() << "Unknown enum value '" << it.Token() << "'"; + flags.set((size_t)e); + } + } +} diff --git a/util/draft/holder_vector.cpp b/util/draft/holder_vector.cpp new file mode 100644 index 00000000000..9994c2a2b56 --- /dev/null +++ b/util/draft/holder_vector.cpp @@ -0,0 +1 @@ +#include "holder_vector.h" diff --git a/util/draft/holder_vector.h b/util/draft/holder_vector.h new file mode 100644 index 00000000000..1c62055bd95 --- /dev/null +++ b/util/draft/holder_vector.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include + +template +class THolderVector: public TVector, public TNonCopyable { + using TBase = TVector; + +public: + explicit THolderVector(size_t n = 0) + : TBase(n) + { + } + + ~THolderVector() { + Clear(); + } + + void Clear() { + for (typename TBase::iterator it = TBase::begin(); it != TBase::end(); ++it) { + if (*it) + D::Destroy(*it); + } + TBase::clear(); + } + + size_t Size() const { + return TBase::size(); + } + + // TVector takes ownership of T + void PushBack(T* t) { + try { + TBase::push_back(t); + } catch (...) { + if (t) + D::Destroy(t); + throw; + } + } + + void PushBack(std::unique_ptr t) { + PushBack(t.release()); + } + + void PushBack(THolder t) { + PushBack(t.Release()); + } + + void Reset(size_t i, THolder t) { + T* current = (*this)[i]; + if (current) { + Y_ASSERT(current != t.Get()); + D::Destroy(current); + } + (*this)[i] = t.Release(); + } + + void PopBack() { + if (size()) { + D::Destroy(back()); + TBase::pop_back(); + } + } + + T* Release(size_t i) { + T* t = (*this)[i]; + (*this)[i] = nullptr; + return t; + } + + void Resize(size_t newSize) { + for (size_t i = newSize; i < size(); ++i) { + D::Destroy((*this)[i]); + } + TBase::resize(newSize); + } + + void Swap(THolderVector& other) { + TBase::swap(other); + } + + using TBase::operator[]; + using TBase::operator bool; + using TBase::at; + using TBase::back; + using TBase::begin; + using TBase::capacity; + using TBase::empty; + using TBase::end; + using TBase::front; + using TBase::reserve; + using TBase::size; + + using typename TBase::const_iterator; + using typename TBase::const_reverse_iterator; + using typename TBase::iterator; + using typename TBase::reverse_iterator; + using typename TBase::value_type; +}; diff --git a/util/draft/holder_vector_ut.cpp b/util/draft/holder_vector_ut.cpp new file mode 100644 index 00000000000..f64393860af --- /dev/null +++ b/util/draft/holder_vector_ut.cpp @@ -0,0 +1,69 @@ +#include "holder_vector.h" + +#include + +Y_UNIT_TEST_SUITE(THolderVectorTest) { + Y_UNIT_TEST(TestCreateEmpty) { + THolderVector ints; + UNIT_ASSERT_EQUAL(ints.Size(), 0); + UNIT_ASSERT(!ints); + } + + Y_UNIT_TEST(TestCreateNonEmpty) { + THolderVector ints(5); + UNIT_ASSERT_EQUAL(ints.Size(), 5); + UNIT_ASSERT(ints); + + for (size_t i = 0; i < ints.Size(); ++i) { + UNIT_ASSERT_EQUAL(ints[i], (int*)nullptr); + } + } + + Y_UNIT_TEST(TestResetValue) { + THolderVector ints; + ints.PushBack(new int(0)); + ints.PushBack(new int(1)); + UNIT_ASSERT_VALUES_EQUAL(*ints[0], 0); + UNIT_ASSERT_VALUES_EQUAL(*ints[1], 1); + ints.Reset(0, MakeHolder(2)); + ints.Reset(1, MakeHolder(3)); + UNIT_ASSERT_VALUES_EQUAL(*ints[0], 2); + UNIT_ASSERT_VALUES_EQUAL(*ints[1], 3); + } + + Y_UNIT_TEST(TestResetNoValue) { + THolderVector ints; + ints.Resize(1); + UNIT_ASSERT_EQUAL(ints[0], (int*)nullptr); + ints.Reset(0, MakeHolder(1)); + UNIT_ASSERT_UNEQUAL(ints[0], (int*)nullptr); + UNIT_ASSERT_VALUES_EQUAL(*ints[0], 1); + } + + Y_UNIT_TEST(TestResetSmartPtr) { + THolderVector ints; + ints.Resize(2); + + THolder holder(new int(1)); + ints.Reset(0, std::move(holder)); + UNIT_ASSERT_VALUES_EQUAL(*ints[0], 1); + UNIT_ASSERT(!holder); + } + + Y_UNIT_TEST(TestSwap) { + THolderVector v1; + v1.PushBack(new int(1)); + + THolderVector v2; + v1.Swap(v2); + UNIT_ASSERT(v1.empty() && v2.size() == 1 && *v2.front() == 1); + } + + Y_UNIT_TEST(TestUniquePtr) { + THolderVector v; + std::unique_ptr str(new TString("hello")); + v.PushBack(std::move(str)); + UNIT_ASSERT(v.Size() == 1); + UNIT_ASSERT(str.get() == nullptr); + } +} diff --git a/util/draft/ip.cpp b/util/draft/ip.cpp new file mode 100644 index 00000000000..a43bcdadcf2 --- /dev/null +++ b/util/draft/ip.cpp @@ -0,0 +1 @@ +#include "ip.h" diff --git a/util/draft/ip.h b/util/draft/ip.h new file mode 100644 index 00000000000..eb947cd2cd5 --- /dev/null +++ b/util/draft/ip.h @@ -0,0 +1,131 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +#ifdef _unix_ + #include + #include + #include +#endif // _unix_ + +#include + +#ifndef INET6_ADDRSTRLEN + #define INET6_ADDRSTRLEN 46 +#endif + +// Network (big-endian) byte order +using TIp4 = TIpHost; + +// Network (big-endian) byte order +struct TIp6 { + char Data[16]; + + bool operator==(const TIp6& rhs) const { + return memcmp(Data, rhs.Data, sizeof(Data)) == 0; + } + + bool operator<(const TIp6& rhs) const { + return memcmp(Data, rhs.Data, sizeof(Data)) < 0; + } +}; + +template <> +struct THash { + inline size_t operator()(const TIp6& ip) const { + return MurmurHash((const void*)ip.Data, 16); + } +}; + +static inline TIp6 Ip6FromIp4(TIp4 addr) { + TIp6 res; + memset(res.Data, 0, sizeof(res.Data)); + res.Data[10] = '\xFF'; + res.Data[11] = '\xFF'; + memcpy(res.Data + 12, &addr, 4); + return res; +} + +static inline TIp6 Ip6FromString(const char* ipStr) { + TIp6 res; + + if (inet_pton(AF_INET6, ipStr, &res.Data) == 0) { + ythrow TSystemError() << "Failed to convert (" << ipStr << ") to ipv6 address"; + } + + return res; +} + +static inline TMaybe TryParseIp6FromString(const char* ipStr) { + TIp6 res; + + if (inet_pton(AF_INET6, ipStr, &res.Data) == 0) { + return Nothing(); + } + + return res; +} + +static inline char* Ip6ToString(const TIp6& ip, char* buf, size_t len) { + if (!inet_ntop(AF_INET6, (void*)&ip.Data, buf, (socklen_t)len)) { + ythrow TSystemError() << "Failed to get ipv6 address string"; + } + + return buf; +} + +static inline TString Ip6ToString(const TIp6& ip) { + char buf[INET6_ADDRSTRLEN]; + + return TString(Ip6ToString(ip, buf, sizeof(buf))); +} + +template <> +inline void Out(IOutputStream& os, const TIp6& a) { + os << Ip6ToString(a); +} + +using TIp4Or6 = std::variant; + +static inline TIp4Or6 Ip4Or6FromString(const char* ipStr) { + const char* c = ipStr; + for (; *c; ++c) { + if (*c == '.') { + return IpFromString(ipStr); + } + if (*c == ':') { + return Ip6FromString(ipStr); + } + } + ythrow TSystemError() << "Failed to convert (" << ipStr << ") to ipv4 or ipv6 address"; +} + +static inline TString Ip4Or6ToString(const TIp4Or6& ip) { + if (std::holds_alternative(ip)) { + return Ip6ToString(std::get(ip)); + } else { + return IpToString(std::get(ip)); + } +} + +// for TIp4 or TIp6, not TIp4Or6 +template +struct TIpCompare { + bool Less(const TIp& l, const TIp& r) const { + return memcmp(&l, &r, sizeof(TIp)) < 0; + } + + bool LessEqual(const TIp& l, const TIp& r) const { + return memcmp(&l, &r, sizeof(TIp)) <= 0; + } + + bool operator()(const TIp& l, const TIp& r) const { + return Less(l, r); + } +}; diff --git a/util/draft/matrix.cpp b/util/draft/matrix.cpp new file mode 100644 index 00000000000..24a274b8101 --- /dev/null +++ b/util/draft/matrix.cpp @@ -0,0 +1 @@ +#include "matrix.h" diff --git a/util/draft/matrix.h b/util/draft/matrix.h new file mode 100644 index 00000000000..154d00b35ea --- /dev/null +++ b/util/draft/matrix.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include +#include + +template +class TMatrix: TNonCopyable { + // Constructor/Destructor +public: + TMatrix() + : Buf(nullptr) + , Arr(nullptr) + , M(0) + , N(0) + , BufSize(0) + { + } + + TMatrix(size_t m, size_t n) + : Buf(new T[m * n]) + , Arr(Buf) + , M(m) + , N(n) + , BufSize(m * n) + { + } + + TMatrix(size_t m, size_t n, T* buf) + : Buf(nullptr) + , Arr(buf) + , M(m) + , N(n) + , BufSize(m * n) + { + } + + ~TMatrix() { + delete[] Buf; + } + + // Properties/Methods +public: + void Clear() { + M = N = 0; + } + + void ReDim(size_t m, size_t n) { + Y_ASSERT(m >= 1 && n >= 1); + size_t newSize = m * n; + if (newSize > BufSize) { + T* newBuf = new T[newSize]; + delete[] Buf; + Arr = Buf = newBuf; + BufSize = newSize; + } + M = m; + N = n; + } + + size_t Width() const { + return N; + } + + size_t Height() const { + return M; + } + + // Access element matrix[i][j] + T* operator[](size_t i) { + Y_ASSERT(i >= 0 && i < M); + return Arr + i * N; + } + + // Access element matrix[i][j] + const T* operator[](size_t i) const { + Y_ASSERT(i >= 0 && i < M); + return Arr + i * N; + } + + // Access element matrix(i, j) + T& operator()(size_t i, size_t j) { + Y_ASSERT(i >= 0 && i < M && j >= 0 && j < N); + return Arr[i * N + j]; + } + + // Access element matrix(i, j) + const T& operator()(size_t i, size_t j) const { + Y_ASSERT(i >= 0 && i < M && j >= 0 && j < N); + return Arr[i * N + j]; + } + + void Zero() { + memset((void*)Arr, 0, M * N * sizeof(T)); + } + + void Fill(T value) { + for (T *p = Arr, *end = Arr + M * N; p < end; ++p) + *p = value; + } + +private: + T* Buf; + T* Arr; + size_t M, N; + size_t BufSize; +}; diff --git a/util/draft/memory.cpp b/util/draft/memory.cpp new file mode 100644 index 00000000000..b31569d4498 --- /dev/null +++ b/util/draft/memory.cpp @@ -0,0 +1 @@ +#include "memory.h" diff --git a/util/draft/memory.h b/util/draft/memory.h new file mode 100644 index 00000000000..0a9722bb36e --- /dev/null +++ b/util/draft/memory.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include +#include +#include + +template +inline bool IsZero(const T* begin, const T* end) { + return std::find_if(begin, end, [](const T& other) { return other != T(0); }) == end; +} + +template +inline bool IsZero(const char* p) { + size_t sizeInUI64 = Size / 8; + const char* pEndUi64 = p + sizeInUI64 * 8; + if (sizeInUI64 && !IsZero((const ui64*)p, (const ui64*)pEndUi64)) + return false; + return IsZero(pEndUi64, p + Size); +} + +#define IS_ZERO_INTSZ(INT) \ + template <> \ + inline bool IsZero(const char* p) { \ + return (*(INT*)p) == INT(0); \ + } + +IS_ZERO_INTSZ(ui8) +IS_ZERO_INTSZ(ui16) +IS_ZERO_INTSZ(ui32) +IS_ZERO_INTSZ(ui64) + +#undef IS_ZERO_INTSZ + +// If you want to use this to check all fields in a struct make sure it's w/o holes or #pragma pack(1) +template +bool IsZero(const T& t) { + return IsZero((const char*)&t); +} diff --git a/util/draft/memory_ut.cpp b/util/draft/memory_ut.cpp new file mode 100644 index 00000000000..76bee30549f --- /dev/null +++ b/util/draft/memory_ut.cpp @@ -0,0 +1,69 @@ +#include "memory.h" + +#include + +#pragma pack(1) +struct Y_PACKED TSampleStruct1 { + ui8 A; + ui8 B; +}; + +#pragma pack(1) +struct Y_PACKED TSampleStruct2 { + ui8 A; + ui16 B; + i32 C; +}; + +#pragma pack(1) +struct Y_PACKED TSampleStruct3 { + TSampleStruct2 A; + ui64 B; +}; + +#pragma pack() + +Y_UNIT_TEST_SUITE(TUtilDraftMemoryTest) { + Y_UNIT_TEST(IsZeroTest) { + ui8 a1 = 0; + UNIT_ASSERT(IsZero(a1)); + a1 = 0xF0; + UNIT_ASSERT(!IsZero(a1)); + + i32 a2 = -1; + UNIT_ASSERT(!IsZero(a2)); + a2 = 0; + UNIT_ASSERT(IsZero(a2)); + + double a3 = 0.0; + UNIT_ASSERT(IsZero(a3)); + a3 = 1.e-13; + UNIT_ASSERT(!IsZero(a3)); + + TSampleStruct1 ss1; + ss1.A = 0; + ss1.B = 0; + UNIT_ASSERT(IsZero(ss1)); + ss1.A = 0; + ss1.B = 12; + UNIT_ASSERT(!IsZero(ss1)); + + TSampleStruct2 ss2; + ss2.A = 0; + ss2.B = 100; + ss2.C = 0; + UNIT_ASSERT(!IsZero(ss2)); + ss2.B = 0; + UNIT_ASSERT(IsZero(ss2)); + + TSampleStruct3 ss3; + ss3.A = ss2; + ss3.B = 0; + UNIT_ASSERT(IsZero(ss3)); + ss3.B = 0x030; + UNIT_ASSERT(!IsZero(ss3)); + ss3.B = 0; + ss3.A.C = -789; + UNIT_ASSERT(!IsZero(ss3)); + } +} diff --git a/util/draft/ut/ya.make b/util/draft/ut/ya.make new file mode 100644 index 00000000000..5a62af41a5e --- /dev/null +++ b/util/draft/ut/ya.make @@ -0,0 +1,18 @@ +UNITTEST() + +SRCDIR(util/draft) + +PEERDIR( + util/draft +) + +SRCS( + date_ut.cpp + datetime_ut.cpp + holder_vector_ut.cpp + memory_ut.cpp +) + +INCLUDE(${ARCADIA_ROOT}/util/tests/ya_util_tests.inc) + +END() diff --git a/util/draft/ya.make b/util/draft/ya.make new file mode 100644 index 00000000000..b9e35cd46bb --- /dev/null +++ b/util/draft/ya.make @@ -0,0 +1,23 @@ +LIBRARY() + +NO_UTIL() + +IF (TSTRING_IS_STD_STRING) + CFLAGS(GLOBAL -DTSTRING_IS_STD_STRING) +ENDIF() + +SRCS( + date.cpp + datetime.cpp + enum.cpp + holder_vector.cpp + ip.cpp + matrix.cpp + memory.cpp +) + +END() + +RECURSE_FOR_TESTS( + ut +) diff --git a/util/folder/dirent_win.c b/util/folder/dirent_win.c new file mode 100644 index 00000000000..7e6db74ce53 --- /dev/null +++ b/util/folder/dirent_win.c @@ -0,0 +1,125 @@ +#include + +#ifdef _win_ + + #include + #include "dirent_win.h" + + #if defined(_MSC_VER) && (_MSC_VER < 1900) +void __cdecl _dosmaperr(unsigned long); + +static void SetErrno() { + _dosmaperr(GetLastError()); +} + #else +void __cdecl __acrt_errno_map_os_error(unsigned long const oserrno); + +static void SetErrno() { + __acrt_errno_map_os_error(GetLastError()); +} + #endif + +struct DIR* opendir(const char* dirname) { + struct DIR* dir = (struct DIR*)malloc(sizeof(struct DIR)); + if (!dir) { + return NULL; + } + dir->sh = INVALID_HANDLE_VALUE; + dir->fff_templ = NULL; + dir->file_no = 0; + dir->readdir_buf = NULL; + + int len = strlen(dirname); + //Remove trailing slashes + while (len && (dirname[len - 1] == '\\' || dirname[len - 1] == '/')) { + --len; + } + int len_converted = MultiByteToWideChar(CP_UTF8, 0, dirname, len, 0, 0); + if (len_converted == 0) { + closedir(dir); + return NULL; + } + dir->fff_templ = (WCHAR*)malloc((len_converted + 5) * sizeof(WCHAR)); + if (!dir->fff_templ) { + closedir(dir); + return NULL; + } + MultiByteToWideChar(CP_UTF8, 0, dirname, len, dir->fff_templ, len_converted); + + WCHAR append[] = {'\\', '*', '.', '*', 0}; + memcpy(dir->fff_templ + len_converted, append, sizeof(append)); + dir->sh = FindFirstFileW(dir->fff_templ, &dir->wfd); + if (dir->sh == INVALID_HANDLE_VALUE) { + SetErrno(); + closedir(dir); + return NULL; + } + + return dir; +} + +int closedir(struct DIR* dir) { + if (dir->sh != INVALID_HANDLE_VALUE) + FindClose(dir->sh); + free(dir->fff_templ); + free(dir->readdir_buf); + free(dir); + return 0; +} + +int readdir_r(struct DIR* dir, struct dirent* entry, struct dirent** result) { + if (!FindNextFileW(dir->sh, &dir->wfd)) { + int err = GetLastError(); + *result = 0; + if (err == ERROR_NO_MORE_FILES) { + SetLastError(0); + return 0; + } else { + return err; + } + } + entry->d_fileno = dir->file_no++; + entry->d_reclen = sizeof(struct dirent); + if (dir->wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && + (dir->wfd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT || dir->wfd.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) + { + entry->d_type = DT_LNK; + } else if (dir->wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + int len = lstrlenW(dir->wfd.cFileName); + int conv_len = WideCharToMultiByte(CP_UTF8, 0, dir->wfd.cFileName, len, 0, 0, 0, 0); + if (conv_len == 0) { + return -1; + } + if (conv_len > sizeof(entry->d_name) - 1) { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return ERROR_INSUFFICIENT_BUFFER; + } + entry->d_namlen = conv_len; + WideCharToMultiByte(CP_UTF8, 0, dir->wfd.cFileName, len, entry->d_name, conv_len, 0, 0); + entry->d_name[conv_len] = 0; + *result = entry; + return 0; +} + +struct dirent* readdir(struct DIR* dir) { + struct dirent* res; + if (!dir->readdir_buf) { + dir->readdir_buf = (struct dirent*)malloc(sizeof(struct dirent)); + if (dir->readdir_buf == 0) + return 0; + } + readdir_r(dir, dir->readdir_buf, &res); + return res; +} + +void rewinddir(struct DIR* dir) { + FindClose(dir->sh); + dir->sh = FindFirstFileW(dir->fff_templ, &dir->wfd); + dir->file_no = 0; +} + +#endif //_win_ diff --git a/util/folder/dirent_win.h b/util/folder/dirent_win.h new file mode 100644 index 00000000000..ac11a64c047 --- /dev/null +++ b/util/folder/dirent_win.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#ifdef _win_ + + #include + + #ifdef __cplusplus +extern "C" { + #endif + + struct DIR { + HANDLE sh; + WIN32_FIND_DATAW wfd; + WCHAR* fff_templ; + int file_no; + struct dirent* readdir_buf; + }; + + #define MAXNAMLEN (MAX_PATH - 1) * 2 + #define MAXPATHLEN MAX_PATH + #define DT_DIR 4 + #define DT_REG 8 + #define DT_LNK 10 + #define DT_UNKNOWN 0 + + struct dirent { + ui32 d_fileno; /* file number of entry */ + ui16 d_reclen; /* length of this record */ + ui8 d_type; /* file type */ + ui8 d_namlen; /* length of string in d_name */ + char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */ + }; + + struct DIR* opendir(const char* dirname); + int closedir(struct DIR* dir); + int readdir_r(struct DIR* dir, struct dirent* entry, struct dirent** result); + struct dirent* readdir(struct DIR* dir); + void rewinddir(struct DIR* dir); + + #ifdef __cplusplus +} + #endif + +#endif //_win_ diff --git a/util/folder/dirut.cpp b/util/folder/dirut.cpp new file mode 100644 index 00000000000..ffc9b09f967 --- /dev/null +++ b/util/folder/dirut.cpp @@ -0,0 +1,621 @@ +#include "dirut.h" +#include "iterator.h" +#include "filelist.h" +#include "fts.h" +#include "pathsplit.h" +#include "path.h" + +#include +#include +#include +#include +#include + +void SlashFolderLocal(TString& folder) { + if (!folder) + return; +#ifdef _win32_ + size_t pos; + while ((pos = folder.find('/')) != TString::npos) + folder.replace(pos, 1, LOCSLASH_S); +#endif + if (folder[folder.size() - 1] != LOCSLASH_C) + folder.append(LOCSLASH_S); +} + +#ifndef _win32_ + +bool correctpath(TString& folder) { + return resolvepath(folder, "/"); +} + +bool resolvepath(TString& folder, const TString& home) { + Y_ASSERT(home && home.at(0) == '/'); + if (!folder) { + return false; + } + // may be from windows + char* ptr = folder.begin(); + while ((ptr = strchr(ptr, '\\')) != nullptr) + *ptr = '/'; + + if (folder.at(0) == '~') { + if (folder.length() == 1 || folder.at(1) == '/') { + folder = GetHomeDir() + (folder.data() + 1); + } else { + char* buf = (char*)alloca(folder.length() + 1); + strcpy(buf, folder.data() + 1); + char* p = strchr(buf, '/'); + if (p) + *p++ = 0; + passwd* pw = getpwnam(buf); + if (pw) { + folder = pw->pw_dir; + folder += "/"; + if (p) + folder += p; + } else { + return false; // unknown user + } + } + } + int len = folder.length() + home.length() + 1; + char* path = (char*)alloca(len); + if (folder.at(0) != '/') { + strcpy(path, home.data()); + strcpy(strrchr(path, '/') + 1, folder.data()); // the last char must be '/' if it's a dir + } else { + strcpy(path, folder.data()); + } + len = strlen(path) + 1; + // grabbed from url.cpp + char* newpath = (char*)alloca(len + 2); + const char** pp = (const char**)alloca(len * sizeof(char*)); + int i = 0; + for (char* s = path; s;) { + pp[i++] = s; + s = strchr(s, '/'); + if (s) + *s++ = 0; + } + + for (int j = 1; j < i;) { + const char*& p = pp[j]; + if (strcmp(p, ".") == 0 || strcmp(p, "") == 0) { + if (j == i - 1) { + p = ""; + break; + } else { + memmove(pp + j, pp + j + 1, (i - j - 1) * sizeof(p)); + --i; + } + } else if (strcmp(p, "..") == 0) { + if (j == i - 1) { + if (j == 1) { + p = ""; + } else { + --i; + pp[j - 1] = ""; + } + break; + } else { + if (j == 1) { + memmove(pp + j, pp + j + 1, (i - j - 1) * sizeof(p)); + --i; + } else { + memmove(pp + j - 1, pp + j + 1, (i - j - 1) * sizeof(p)); + i -= 2; + --j; + } + } + } else + ++j; + } + + char* s = newpath; + for (int k = 0; k < i; k++) { + s = strchr(strcpy(s, pp[k]), 0); + *s++ = '/'; + } + *(--s) = 0; + folder = newpath; + return true; +} + +#else + +using dir_type = enum { + dt_empty, + dt_error, + dt_up, + dt_dir +}; + +// precondition: *ptr != '\\' || *ptr == 0 (cause dt_error) +// postcondition: *ptr != '\\' +template +static int next_dir(T*& ptr) { + int has_blank = 0; + int has_dot = 0; + int has_letter = 0; + int has_ctrl = 0; + + while (*ptr && *ptr != '\\') { + int c = (unsigned char)*ptr++; + switch (c) { + case ' ': + ++has_blank; + break; + case '.': + ++has_dot; + break; + case '/': + case ':': + case '*': + case '?': + case '"': + case '<': + case '>': + case '|': + ++has_ctrl; + break; + default: + if (c == 127 || c < ' ') + ++has_ctrl; + else + ++has_letter; + } + } + if (*ptr) + ++ptr; + if (has_ctrl) + return dt_error; + if (has_letter) + return dt_dir; + if (has_dot && has_blank) + return dt_error; + if (has_dot == 1) + return dt_empty; + if (has_dot == 2) + return dt_up; + return dt_error; +} + +using disk_type = enum { + dk_noflags = 0, + dk_unc = 1, + dk_hasdrive = 2, + dk_fromroot = 4, + dk_error = 8 +}; + +// root slash (if any) - part of disk +template +static int skip_disk(T*& ptr) { + int result = dk_noflags; + if (!*ptr) + return result; + if (ptr[0] == '\\' && ptr[1] == '\\') { + result |= dk_unc | dk_fromroot; + ptr += 2; + if (next_dir(ptr) != dt_dir) + return dk_error; // has no host name + if (next_dir(ptr) != dt_dir) + return dk_error; // has no share name + } else { + if (*ptr && *(ptr + 1) == ':') { + result |= dk_hasdrive; + ptr += 2; + } + if (*ptr == '\\' || *ptr == '/') { + ++ptr; + result |= dk_fromroot; + } + } + return result; +} + +int correctpath(char* cpath, const char* path) { + if (!path || !*path) { + *cpath = 0; + return 1; + } + char* ptr = (char*)path; + char* cptr = cpath; + int counter = 0; + while (*ptr) { + char c = *ptr; + if (c == '/') + c = '\\'; + if (c == '\\') + ++counter; + else + counter = 0; + if (counter <= 1) { + *cptr = c; + ++cptr; + } + ++ptr; + } + *cptr = 0; + // replace '/' by '\' + int dk = skip_disk(cpath); + + if (dk == dk_error) + return 0; + + char* ptr1 = ptr = cpath; + int level = 0; + while (*ptr) { + switch (next_dir(ptr)) { + case dt_dir: + ++level; + break; + case dt_empty: + memmove(ptr1, ptr, strlen(ptr) + 1); + ptr = ptr1; + break; + case dt_up: + --level; + if (level >= 0) { + *--ptr1 = 0; + ptr1 = strrchr(cpath, '\\'); + if (!ptr1) + ptr1 = cpath; + else + ++ptr1; + memmove(ptr1, ptr, strlen(ptr) + 1); + ptr = ptr1; + break; + } else if (level == -1 && (dk & dk_hasdrive)) { + if (*ptr && *(ptr + 1) == ':' && *(cpath - 2) == ':') { + memmove(cpath - 3, ptr, strlen(ptr) + 1); + return 1; + } + } + if (dk & dk_fromroot) + return 0; + break; + case dt_error: + default: + return 0; + } + ptr1 = ptr; + } + + if ((ptr > cpath || ptr == cpath && dk & dk_unc) && *(ptr - 1) == '\\') + *(ptr - 1) = 0; + return 1; +} + +static inline int normchar(unsigned char c) { + return (c < 'a' || c > 'z') ? c : c - 32; +} + +static inline char* strslashcat(char* a, const char* b) { + size_t len = strlen(a); + if (len && a[len - 1] != '\\') + a[len++] = '\\'; + strcpy(a + len, b); + return a; +} + +int resolvepath(char* apath, const char* rpath, const char* cpath) { + const char* redisk = rpath; + if (!rpath || !*rpath) + return 0; + int rdt = skip_disk(redisk); + if (rdt == dk_error) + return 0; + if (rdt & dk_unc || rdt & dk_hasdrive && rdt & dk_fromroot) { + return correctpath(apath, rpath); + } + + const char* cedisk = cpath; + if (!cpath || !*cpath) + return 0; + int cdt = skip_disk(cedisk); + if (cdt == dk_error) + return 0; + + char* tpath = (char*)alloca(strlen(rpath) + strlen(cpath) + 3); + + // rdt&dk_hasdrive && !rdt&dk_fromroot + if (rdt & dk_hasdrive) { + if (!(cdt & dk_fromroot)) + return 0; + if (cdt & dk_hasdrive && normchar(*rpath) != normchar(*cpath)) + return 0; + memcpy(tpath, rpath, 2); + memcpy(tpath + 2, cedisk, strlen(cedisk) + 1); + strslashcat(tpath, redisk); + + // !rdt&dk_hasdrive && rdt&dk_fromroot + } else if (rdt & dk_fromroot) { + if (!(cdt & dk_hasdrive) && !(cdt & dk_unc)) + return 0; + memcpy(tpath, cpath, cedisk - cpath); + tpath[cedisk - cpath] = 0; + strslashcat(tpath, redisk); + + // !rdt&dk_hasdrive && !rdt&dk_fromroot + } else { + if (!(cdt & dk_fromroot) || !(cdt & dk_hasdrive) && !(cdt & dk_unc)) + return 0; + strslashcat(strcpy(tpath, cpath), redisk); + } + + return correctpath(apath, tpath); +} + +bool correctpath(TString& filename) { + char* ptr = (char*)alloca(filename.size() + 2); + if (correctpath(ptr, filename.data())) { + filename = ptr; + return true; + } + return false; +} + +bool resolvepath(TString& folder, const TString& home) { + char* ptr = (char*)alloca(folder.size() + 3 + home.size()); + if (resolvepath(ptr, folder.data(), home.data())) { + folder = ptr; + return true; + } + return false; +} + +#endif // !defined _win32_ + +char GetDirectorySeparator() { + return LOCSLASH_C; +} + +const char* GetDirectorySeparatorS() { + return LOCSLASH_S; +} + +void RemoveDirWithContents(TString dirName) { + SlashFolderLocal(dirName); + + TDirIterator dir(dirName, TDirIterator::TOptions(FTS_NOSTAT)); + + for (auto it = dir.begin(); it != dir.end(); ++it) { + switch (it->fts_info) { + case FTS_F: + case FTS_DEFAULT: + case FTS_DP: + case FTS_SL: + case FTS_SLNONE: + if (!NFs::Remove(it->fts_path)) + ythrow TSystemError() << "error while removing " << it->fts_path; + break; + } + } +} + +int mkpath(char* path, int mode) { + return NFs::MakeDirectoryRecursive(path, NFs::EFilePermission(mode)) ? 0 : -1; +} + +// Implementation of realpath in FreeBSD (version 9.0 and less) and GetFullPathName in Windows +// did not require last component of the file name to exist (other implementations will fail +// if it does not). Use RealLocation if that behaviour is required. +TString RealPath(const TString& path) { + TTempBuf result; + Y_ASSERT(result.Size() > MAX_PATH); //TMP_BUF_LEN > MAX_PATH +#ifdef _win_ + if (GetFullPathName(path.data(), result.Size(), result.Data(), nullptr) == 0) +#else + if (realpath(path.data(), result.Data()) == nullptr) +#endif + ythrow TFileError() << "RealPath failed \"" << path << "\""; + return result.Data(); +} + +TString RealLocation(const TString& path) { + if (NFs::Exists(path)) + return RealPath(path); + TString dirpath = GetDirName(path); + if (NFs::Exists(dirpath)) + return RealPath(dirpath) + GetDirectorySeparatorS() + GetFileNameComponent(path.data()); + ythrow TFileError() << "RealLocation failed \"" << path << "\""; +} + +int MakeTempDir(char path[/*FILENAME_MAX*/], const char* prefix) { + int ret; + + TString sysTmp; + +#ifdef _win32_ + if (!prefix || *prefix == '/') { +#else + if (!prefix) { +#endif + sysTmp = GetSystemTempDir(); + prefix = sysTmp.data(); + } + + if ((ret = ResolvePath(prefix, nullptr, path, 1)) != 0) + return ret; + if (!TFileStat(path).IsDir()) + return ENOENT; + if ((strlcat(path, "tmpXXXXXX", FILENAME_MAX) > FILENAME_MAX - 100)) + return EINVAL; + if (!(mkdtemp(path))) + return errno ? errno : EINVAL; + strcat(path, LOCSLASH_S); + return 0; +} + +bool IsDir(const TString& path) { + return TFileStat(path).IsDir(); +} + +TString GetHomeDir() { + TString s(getenv("HOME")); + if (!s) { +#ifndef _win32_ + passwd* pw = nullptr; + s = getenv("USER"); + if (s) + pw = getpwnam(s.data()); + else + pw = getpwuid(getuid()); + if (pw) + s = pw->pw_dir; + else +#endif + { + char* cur_dir = getcwd(nullptr, 0); + s = cur_dir; + free(cur_dir); + } + } + return s; +} + +void MakeDirIfNotExist(const char* path, int mode) { + if (!NFs::MakeDirectory(path, NFs::EFilePermission(mode)) && !NFs::Exists(path)) { + ythrow TSystemError() << "failed to create directory " << path; + } +} + +void MakePathIfNotExist(const char* path, int mode) { + NFs::MakeDirectoryRecursive(path, NFs::EFilePermission(mode)); + if (!NFs::Exists(path) || !TFileStat(path).IsDir()) { + ythrow TSystemError() << "failed to create directory " << path; + } +} + +const char* GetFileNameComponent(const char* f) { + const char* p = strrchr(f, LOCSLASH_C); +#ifdef _win_ + // "/" is also valid char separator on Windows + const char* p2 = strrchr(f, '/'); + if (p2 > p) + p = p2; +#endif + + if (p) { + return p + 1; + } + + return f; +} + +TString GetSystemTempDir() { +#ifdef _win_ + char buffer[1024]; + DWORD size = GetTempPath(1024, buffer); + if (!size) { + ythrow TSystemError() << "failed to get system temporary directory"; + } + return TString(buffer, size); +#else + const char* var = "TMPDIR"; + const char* def = "/tmp"; + const char* r = getenv(var); + const char* result = r ? r : def; + return result[0] == '/' ? result : ResolveDir(result); +#endif +} + +TString ResolveDir(const char* path) { + return ResolvePath(path, true); +} + +bool SafeResolveDir(const char* path, TString& result) { + try { + result = ResolvePath(path, true); + return true; + } catch (...) { + return false; + } +} + +TString GetDirName(const TString& path) { + return TFsPath(path).Dirname(); +} + +#ifdef _win32_ + +char* realpath(const char* pathname, char resolved_path[MAXPATHLEN]) { + // partial implementation: no path existence check + return _fullpath(resolved_path, pathname, MAXPATHLEN - 1); +} + +#endif + +TString GetBaseName(const TString& path) { + return TFsPath(path).Basename(); +} + +static bool IsAbsolutePath(const char* str) { + return str && TPathSplitTraitsLocal::IsAbsolutePath(TStringBuf(str, NStringPrivate::GetStringLengthWithLimit(str, 3))); +} + +int ResolvePath(const char* rel, const char* abs, char res[/*MAXPATHLEN*/], bool isdir) { + char t[MAXPATHLEN * 2 + 3]; + size_t len; + + *res = 0; + if (!rel || !*rel) + return EINVAL; + if (!IsAbsolutePath(rel) && IsAbsolutePath(abs)) { + len = strlcpy(t, abs, sizeof(t)); + if (len >= sizeof(t) - 3) + return EINVAL; + if (t[len - 1] != LOCSLASH_C) + t[len++] = LOCSLASH_C; + len += strlcpy(t + len, rel, sizeof(t) - len); + } else { + len = strlcpy(t, rel, sizeof(t)); + } + if (len >= sizeof(t) - 3) + return EINVAL; + if (isdir && t[len - 1] != LOCSLASH_C) { + t[len++] = LOCSLASH_C; + t[len] = 0; + } + if (!realpath(t, res)) { + if (!isdir && realpath(GetDirName(t).data(), res)) { + len = strlen(res); + if (res[len - 1] != LOCSLASH_C) { + res[len++] = LOCSLASH_C; + res[len] = 0; + } + strcpy(res + len, GetBaseName(t).data()); + return 0; + } + return errno ? errno : ENOENT; + } + if (isdir) { + len = strlen(res); + if (res[len - 1] != LOCSLASH_C) { + res[len++] = LOCSLASH_C; + res[len] = 0; + } + } + return 0; +} + +TString ResolvePath(const char* rel, const char* abs, bool isdir) { + char buf[PATH_MAX]; + if (ResolvePath(rel, abs, buf, isdir)) + ythrow yexception() << "cannot resolve path: \"" << rel << "\""; + return buf; +} + +TString ResolvePath(const char* path, bool isDir) { + return ResolvePath(path, nullptr, isDir); +} + +TString StripFileComponent(const TString& fileName) { + TString dir = IsDir(fileName) ? fileName : GetDirName(fileName); + if (!dir.empty() && dir.back() != GetDirectorySeparator()) { + dir.append(GetDirectorySeparator()); + } + return dir; +} diff --git a/util/folder/dirut.h b/util/folder/dirut.h new file mode 100644 index 00000000000..2537027b12d --- /dev/null +++ b/util/folder/dirut.h @@ -0,0 +1,119 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#ifdef _win32_ + #include + #include + #include + #include + #include + #include "dirent_win.h" + +// these live in mktemp_system.cpp +extern "C" int mkstemps(char* path, int slen); +char* mkdtemp(char* path); + +#else + #ifdef _sun_ + #include + +char* mkdtemp(char* path); + #endif + #include + #include + #include + #ifndef DT_DIR + #include + #endif +#endif + +bool IsDir(const TString& path); + +int mkpath(char* path, int mode = 0777); + +TString GetHomeDir(); + +void MakeDirIfNotExist(const char* path, int mode = 0777); + +inline void MakeDirIfNotExist(const TString& path, int mode = 0777) { + MakeDirIfNotExist(path.data(), mode); +} + +/// Create path making parent directories as needed +void MakePathIfNotExist(const char* path, int mode = 0777); + +void SlashFolderLocal(TString& folder); +bool correctpath(TString& filename); +bool resolvepath(TString& folder, const TString& home); + +char GetDirectorySeparator(); +const char* GetDirectorySeparatorS(); + +void RemoveDirWithContents(TString dirName); + +const char* GetFileNameComponent(const char* f); + +inline TString GetFileNameComponent(const TString& f) { + return GetFileNameComponent(f.data()); +} + +/// RealPath doesn't guarantee trailing separator to be stripped or left in place for directories. +TString RealPath(const TString& path); // throws +TString RealLocation(const TString& path); /// throws; last file name component doesn't need to exist + +TString GetSystemTempDir(); + +int MakeTempDir(char path[/*FILENAME_MAX*/], const char* prefix); + +int ResolvePath(const char* rel, const char* abs, char res[/*FILENAME_MAX*/], bool isdir = false); +TString ResolvePath(const char* rel, const char* abs, bool isdir = false); +TString ResolvePath(const char* path, bool isDir = false); + +TString ResolveDir(const char* path); + +bool SafeResolveDir(const char* path, TString& result); + +TString GetDirName(const TString& path); + +TString GetBaseName(const TString& path); + +TString StripFileComponent(const TString& fileName); + +class TExistenceChecker { +public: + TExistenceChecker(bool strict = false) + : Strict(strict) + { + } + + void SetStrict(bool strict) { + Strict = strict; + } + + bool IsStrict() const { + return Strict; + } + + const char* Check(const char* fname) const { + if (!fname || !*fname) + return nullptr; + if (Strict) { + NFs::EnsureExists(fname); + } else if (!NFs::Exists(fname)) + fname = nullptr; + return fname; + } + +private: + bool Strict; +}; diff --git a/util/folder/dirut_ut.cpp b/util/folder/dirut_ut.cpp new file mode 100644 index 00000000000..dbfabdd059c --- /dev/null +++ b/util/folder/dirut_ut.cpp @@ -0,0 +1,132 @@ +#include "dirut.h" +#include "tempdir.h" + +#include + +#include +#include +#include + +Y_UNIT_TEST_SUITE(TDirutTest) { + Y_UNIT_TEST(TestRealPath) { + UNIT_ASSERT(IsDir(RealPath("."))); + } + + Y_UNIT_TEST(TestRealLocation) { + UNIT_ASSERT(IsDir(RealLocation("."))); + + TTempDir tempDir; + TString base = RealPath(tempDir()); + UNIT_ASSERT(!base.empty()); + + if (base.back() == GetDirectorySeparator()) { + base.pop_back(); + } + + TString path; + TString pathNotNorm; + + path = base + GetDirectorySeparatorS() + "no_such_file"; + UNIT_ASSERT(NFs::Exists(GetDirName(path))); + UNIT_ASSERT(!NFs::Exists(path)); + path = RealLocation(path); + UNIT_ASSERT(NFs::Exists(GetDirName(path))); + UNIT_ASSERT(!NFs::Exists(path)); + UNIT_ASSERT_EQUAL(GetDirName(path), base); + + pathNotNorm = base + GetDirectorySeparatorS() + "some_dir" + GetDirectorySeparatorS() + ".." + GetDirectorySeparatorS() + "no_such_file"; + MakeDirIfNotExist((base + GetDirectorySeparatorS() + "some_dir").data()); + pathNotNorm = RealLocation(pathNotNorm); + UNIT_ASSERT(NFs::Exists(GetDirName(pathNotNorm))); + UNIT_ASSERT(!NFs::Exists(pathNotNorm)); + UNIT_ASSERT_EQUAL(GetDirName(pathNotNorm), base); + + UNIT_ASSERT_EQUAL(path, pathNotNorm); + + path = base + GetDirectorySeparatorS() + "file"; + { + TFixedBufferFileOutput file(path); + } + UNIT_ASSERT(NFs::Exists(GetDirName(path))); + UNIT_ASSERT(NFs::Exists(path)); + UNIT_ASSERT(NFs::Exists(path)); + path = RealLocation(path); + UNIT_ASSERT(NFs::Exists(GetDirName(path))); + UNIT_ASSERT(NFs::Exists(path)); + UNIT_ASSERT_EQUAL(GetDirName(path), base); + } + + void DoTest(const char* p, const char* base, const char* canon) { + TString path(p); + UNIT_ASSERT(resolvepath(path, base)); + UNIT_ASSERT(path == canon); + } + + Y_UNIT_TEST(TestResolvePath) { +#ifdef _win_ + DoTest("bar", "c:\\foo\\baz", "c:\\foo\\baz\\bar"); + DoTest("c:\\foo\\bar", "c:\\bar\\baz", "c:\\foo\\bar"); +#else + DoTest("bar", "/foo/baz", "/foo/bar"); + DoTest("/foo/bar", "/bar/baz", "/foo/bar"); + + #ifdef NDEBUG + DoTest("bar", "./baz", "./bar"); + #if 0 // should we support, for consistency, single-label dirs + DoTest("bar", "baz", "bar"); + #endif + #endif +#endif + } + + Y_UNIT_TEST(TestResolvePathRelative) { + TTempDir tempDir; + TTempBuf tempBuf; + TString base = RealPath(tempDir()); + if (base.back() == GetDirectorySeparator()) { + base.pop_back(); + } + + // File + TString path = base + GetDirectorySeparatorS() + "file"; + { + TFixedBufferFileOutput file(path); + } + ResolvePath("file", base.data(), tempBuf.Data(), false); + UNIT_ASSERT_EQUAL(tempBuf.Data(), path); + + // Dir + path = base + GetDirectorySeparatorS() + "dir"; + MakeDirIfNotExist(path.data()); + ResolvePath("dir", base.data(), tempBuf.Data(), true); + UNIT_ASSERT_EQUAL(tempBuf.Data(), path + GetDirectorySeparatorS()); + + // Absent file in existent dir + path = base + GetDirectorySeparatorS() + "nofile"; + ResolvePath("nofile", base.data(), tempBuf.Data(), false); + UNIT_ASSERT_EQUAL(tempBuf.Data(), path); + } + + Y_UNIT_TEST(TestGetDirName) { + UNIT_ASSERT_VALUES_EQUAL(".", GetDirName("parambambam")); + } + + Y_UNIT_TEST(TestStripFileComponent) { + static const TString tmpDir = "tmp_dir_for_tests"; + static const TString tmpSubDir = tmpDir + GetDirectorySeparatorS() + "subdir"; + static const TString tmpFile = tmpDir + GetDirectorySeparatorS() + "file"; + + // creating tmp dir and subdirs + MakeDirIfNotExist(tmpDir.data()); + MakeDirIfNotExist(tmpSubDir.data()); + { + TFixedBufferFileOutput file(tmpFile); + } + + UNIT_ASSERT_EQUAL(StripFileComponent(tmpDir), tmpDir + GetDirectorySeparatorS()); + UNIT_ASSERT_EQUAL(StripFileComponent(tmpSubDir), tmpSubDir + GetDirectorySeparatorS()); + UNIT_ASSERT_EQUAL(StripFileComponent(tmpFile), tmpDir + GetDirectorySeparatorS()); + + RemoveDirWithContents(tmpDir); + } +} diff --git a/util/folder/filelist.cpp b/util/folder/filelist.cpp new file mode 100644 index 00000000000..b21fcdbf208 --- /dev/null +++ b/util/folder/filelist.cpp @@ -0,0 +1,40 @@ +#include "dirut.h" +#include "filelist.h" +#include "iterator.h" + +#include + +void TFileEntitiesList::Fill(const TString& dirname, TStringBuf prefix, TStringBuf suffix, int depth, bool sort) { + TDirIterator::TOptions opts; + opts.SetMaxLevel(depth); + if (sort) { + opts.SetSortByName(); + } + + TDirIterator dir(dirname, opts); + Clear(); + + size_t dirNameLength = dirname.length(); + while (dirNameLength && (dirname[dirNameLength - 1] == '\\' || dirname[dirNameLength - 1] == '/')) { + --dirNameLength; + } + + for (auto file = dir.begin(); file != dir.end(); ++file) { + if (file->fts_pathlen == file->fts_namelen || file->fts_pathlen <= dirNameLength) { + continue; + } + + TStringBuf filename = file->fts_path + dirNameLength + 1; + + if (filename.empty() || !filename.StartsWith(prefix) || !filename.EndsWith(suffix)) { + continue; + } + + if (((Mask & EM_FILES) && file->fts_info == FTS_F) || ((Mask & EM_DIRS) && file->fts_info == FTS_D) || ((Mask & EM_SLINKS) && file->fts_info == FTS_SL)) { + ++FileNamesSize; + FileNames.Append(filename.data(), filename.size() + 1); + } + } + + Restart(); +} diff --git a/util/folder/filelist.h b/util/folder/filelist.h new file mode 100644 index 00000000000..8851323babd --- /dev/null +++ b/util/folder/filelist.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include + +class TFileEntitiesList { +public: + enum EMaskFlag { + EM_FILES = 1, + EM_DIRS = 2, + EM_SLINKS = 4, + + EM_FILES_DIRS = EM_FILES | EM_DIRS, + EM_FILES_SLINKS = EM_FILES | EM_SLINKS, + EM_DIRS_SLINKS = EM_DIRS | EM_SLINKS, + EM_FILES_DIRS_SLINKS = EM_FILES | EM_DIRS | EM_SLINKS + }; + Y_DECLARE_FLAGS(EMask, EMaskFlag); + + TFileEntitiesList(EMask mask) + : Mask(mask) + { + Clear(); + } + + void Clear() { + Cur = nullptr; + FileNamesSize = CurName = 0; + FileNames.Clear(); + FileNames.Append("", 1); + } + + const char* Next() { + return Cur = (CurName++ < FileNamesSize ? strchr(Cur, 0) + 1 : nullptr); + } + + size_t Size() { + return FileNamesSize; + } + + inline void Fill(const TString& dirname, bool sort = false) { + Fill(dirname, TStringBuf(), sort); + } + + inline void Fill(const TString& dirname, TStringBuf prefix, bool sort = false) { + Fill(dirname, prefix, TStringBuf(), 1, sort); + } + + void Fill(const TString& dirname, TStringBuf prefix, TStringBuf suffix, int depth, bool sort = false); + + void Restart() { + Cur = FileNames.Data(); + CurName = 0; + } + +protected: + TBuffer FileNames; + size_t FileNamesSize, CurName; + const char* Cur; + EMask Mask; +}; + +Y_DECLARE_OPERATORS_FOR_FLAGS(TFileEntitiesList::EMask); + +class TFileList: public TFileEntitiesList { +public: + TFileList() + : TFileEntitiesList(TFileEntitiesList::EM_FILES) + { + } +}; + +class TDirsList: public TFileEntitiesList { +public: + TDirsList() + : TFileEntitiesList(TFileEntitiesList::EM_DIRS) + { + } +}; diff --git a/util/folder/filelist_ut.cpp b/util/folder/filelist_ut.cpp new file mode 100644 index 00000000000..0cdcdf3d003 --- /dev/null +++ b/util/folder/filelist_ut.cpp @@ -0,0 +1,56 @@ +#include "dirut.h" +#include "filelist.h" +#include "tempdir.h" + +#include + +#include +#include + +class TFileListTest: public TTestBase { + UNIT_TEST_SUITE(TFileListTest); + UNIT_TEST(TestSimple); + UNIT_TEST(TestPrefix); + UNIT_TEST_SUITE_END(); + +public: + void TestSimple(); + void TestPrefix(); +}; + +void TFileListTest::TestSimple() { + TTempDir tempDir("nonexistingdir"); + MakeDirIfNotExist((tempDir() + LOCSLASH_S "subdir").data()); + TFile((tempDir() + LOCSLASH_S "subdir" LOCSLASH_S "file").data(), CreateAlways); + + TFileList fileList; + fileList.Fill(tempDir().data(), "", "", 1000); + TString fileName(fileList.Next()); + UNIT_ASSERT_EQUAL(fileName, "subdir" LOCSLASH_S "file"); + UNIT_ASSERT_EQUAL(fileList.Next(), nullptr); +} + +void TFileListTest::TestPrefix() { + TTempDir tempDir("nonexistingdir"); + TFile((tempDir() + LOCSLASH_S "good_file1").data(), CreateAlways); + TFile((tempDir() + LOCSLASH_S "good_file2").data(), CreateAlways); + TFile((tempDir() + LOCSLASH_S "bad_file1").data(), CreateAlways); + TFile((tempDir() + LOCSLASH_S "bad_file2").data(), CreateAlways); + + const bool SORT = true; + TFileList fileList; + { + fileList.Fill(tempDir().data(), "good_file", SORT); + UNIT_ASSERT_EQUAL(TString(fileList.Next()), "good_file1"); + UNIT_ASSERT_EQUAL(TString(fileList.Next()), "good_file2"); + UNIT_ASSERT_EQUAL(fileList.Next(), nullptr); + } + { + fileList.Fill(tempDir().data(), "bad_file", SORT); + UNIT_ASSERT_EQUAL(TString(fileList.Next()), "bad_file1"); + UNIT_ASSERT_EQUAL(TString(fileList.Next()), "bad_file2"); + UNIT_ASSERT_EQUAL(fileList.Next(), nullptr); + } +} + +UNIT_TEST_SUITE_REGISTRATION(TFileListTest); diff --git a/util/folder/fts.cpp b/util/folder/fts.cpp new file mode 100644 index 00000000000..609a56b076f --- /dev/null +++ b/util/folder/fts.cpp @@ -0,0 +1,1434 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $OpenBSD: fts.c,v 1.22 1999/10/03 19:22:22 millert Exp $ + */ + +#include +#include +#include +#include +#include + +#include +#ifndef _win_ + #include + #include + #include + #include + #include + #include +#else + #include + #include "dirent_win.h" + #include "lstat_win.h" +#endif + +#include + +#include + +#include "fts.h" +#include + +#ifndef _win_ + +static const dird invalidDirD = -1; + +dird get_cwdd() { + return open(".", O_RDONLY, 0); +} + +dird get_dird(char* path) { + return open(path, O_RDONLY, 0); +} + +int valid_dird(dird fd) { + return fd < 0; +} + +void close_dird(dird fd) { + (void)close(fd); +} + +int chdir_dird(dird fd) { + return fchdir(fd); +} + +int cmp_dird(dird fd1, dird fd2) { + return fd1 - fd2; +} + +#else // ndef _win_ + +int stat64UTF(const char* path, struct _stat64* _Stat) { + int len_converted = MultiByteToWideChar(CP_UTF8, 0, path, -1, 0, 0); + if (len_converted == 0) { + return -1; + } + WCHAR* buf = (WCHAR*)malloc(sizeof(WCHAR) * (len_converted)); + if (buf == nullptr) { + return -1; + } + MultiByteToWideChar(CP_UTF8, 0, path, -1, buf, len_converted); + + int ret = _wstat64(buf, _Stat); + free(buf); + return ret; +} + +int stat64UTF(dird path, struct _stat64* _Stat) { + return _wstat64(path, _Stat); +} + +const dird invalidDirD = nullptr; + +dird get_cwdd() { + return _wgetcwd(nullptr, 0); +} + +int valid_dird(dird fd) { + return fd == nullptr; +} + +void close_dird(dird fd) { + free(fd); +} + +int chdir_dird(dird fd) { + return _wchdir(fd); +} + +int chdir_dird(const char* path) { + int len_converted = MultiByteToWideChar(CP_UTF8, 0, path, -1, 0, 0); + if (len_converted == 0) { + return -1; + } + WCHAR* buf = (WCHAR*)malloc(sizeof(WCHAR) * (len_converted)); + if (buf == nullptr) { + return -1; + } + MultiByteToWideChar(CP_UTF8, 0, path, -1, buf, len_converted); + int ret = _wchdir(buf); + free(buf); + return ret; +} + +int cmp_dird(dird fd1, dird fd2) { + return lstrcmpW(fd1, fd2); +} + +dird get_dird(char* path) { + int len_converted = MultiByteToWideChar(CP_UTF8, 0, path, -1, 0, 0); + if (len_converted == 0) { + return nullptr; + } + WCHAR* buf = (WCHAR*)malloc(sizeof(WCHAR) * (len_converted)); + if (buf == nullptr) { + return nullptr; + } + MultiByteToWideChar(CP_UTF8, 0, path, -1, buf, len_converted); + + WCHAR* ret = _wfullpath(0, buf, 0); + + free(buf); + return ret; +} + +#endif // ndef _win_ + +#ifdef _win_ + #define S_ISDIR(st_mode) ((st_mode & _S_IFMT) == _S_IFDIR) + #define S_ISREG(st_mode) ((st_mode & _S_IFMT) == _S_IFREG) + #define S_ISLNK(st_mode) ((st_mode & _S_IFMT) == _S_IFLNK) + #define O_RDONLY _O_RDONLY +static int fts_safe_changedir(FTS*, FTSENT*, int, dird); +#endif + +#if defined(__svr4__) || defined(__linux__) || defined(__CYGWIN__) || defined(_win_) || defined(_emscripten_) + #ifdef MAX + #undef MAX + #endif + #define MAX(a, b) ((a) > (b) ? (a) : (b)) + #undef ALIGNBYTES + #undef ALIGN + #define ALIGNBYTES (sizeof(long long) - 1) + #define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) & ~ALIGNBYTES) + #if !defined(__linux__) && !defined(__CYGWIN__) && !defined(_emscripten_) + #define dirfd(dirp) ((dirp)->dd_fd) + #endif + #define D_NAMLEN(dirp) (strlen(dirp->d_name)) +#else + #define D_NAMLEN(dirp) (dirp->d_namlen) +#endif + +static FTSENT* fts_alloc(FTS*, const char*, int); +static FTSENT* fts_build(FTS*, int); +static void fts_lfree(FTSENT*); +static void fts_load(FTS*, FTSENT*); +static size_t fts_maxarglen(char* const*); +static void fts_padjust(FTS*); +static int fts_palloc(FTS*, size_t); +static FTSENT* fts_sort(FTS*, FTSENT*, int); +static u_short fts_stat(FTS*, FTSENT*, int); +static int fts_safe_changedir(FTS*, FTSENT*, int, const char*); + +#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) + +#define CLR(opt) (sp->fts_options &= ~(opt)) +#define ISSET(opt) (sp->fts_options & (opt)) +#define SET(opt) (sp->fts_options |= (opt)) + +#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && chdir_dird(fd)) + +/* fts_build flags */ +#define BCHILD 1 /* yfts_children */ +#define BNAMES 2 /* yfts_children, names only */ +#define BREAD 3 /* yfts_read */ + +static u_short +yfts_type_from_info(u_short info) { + if (info == FTS_D || info == FTS_DP || info == FTS_DOT) { + return FTS_D; + } else if (info == FTS_F) { + return FTS_F; + } else if (info == FTS_SL || info == FTS_SLNONE) { + return FTS_SL; + } + return FTS_NSOK; +} + +static void* +yreallocf(void* ptr, size_t size) +{ + void* nptr; + + nptr = realloc(ptr, size); + if (!nptr && ptr) { + free(ptr); + } + return (nptr); +} + +FTS* yfts_open(char* const* argv, int options, int (*compar)(const FTSENT**, const FTSENT**)) +{ + FTS* sp; + FTSENT *p, *root; + int nitems; + FTSENT *parent, *tmp; + int len; + + errno = 0; + + Y_ASSERT(argv); + if (!*argv) { + errno = ENOENT; + return nullptr; + } + + /* Options check. */ + if (options & ~FTS_OPTIONMASK) { + errno = EINVAL; + return nullptr; + } + + /* Allocate/initialize the stream */ + if ((sp = (FTS*)malloc(sizeof(FTS))) == nullptr) { + return nullptr; + } + memset(sp, 0, sizeof(FTS)); + sp->fts_compar = compar; + sp->fts_options = options; + + /* Shush, GCC. */ + tmp = nullptr; + + /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ + if (ISSET(FTS_LOGICAL)) { + SET(FTS_NOCHDIR); + } + + /* + * Start out with 1K of path space, and enough, in any case, + * to hold the user's paths. + */ + if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) { + goto mem1; + } + + /* Allocate/initialize root's parent. */ + if ((parent = fts_alloc(sp, "", 0)) == nullptr) { + goto mem2; + } + parent->fts_level = FTS_ROOTPARENTLEVEL; + + /* Allocate/initialize root(s). */ + for (root = nullptr, nitems = 0; *argv; ++argv, ++nitems) { + /* Don't allow zero-length paths. */ + + len = strlen(*argv); + +//Any subsequent windows call will expect no trailing slashes so we will remove them here +#ifdef _win_ + while (len && ((*argv)[len - 1] == '\\' || (*argv)[len - 1] == '/')) { + --len; + } +#endif + + if (len == 0) { + errno = ENOENT; + goto mem3; + } + + p = fts_alloc(sp, *argv, len); + p->fts_level = FTS_ROOTLEVEL; + p->fts_parent = parent; + p->fts_accpath = p->fts_name; + p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); + p->fts_type = yfts_type_from_info(p->fts_info); + + /* Command-line "." and ".." are real directories. */ + if (p->fts_info == FTS_DOT) { + p->fts_info = FTS_D; + } + + /* + * If comparison routine supplied, traverse in sorted + * order; otherwise traverse in the order specified. + */ + if (compar) { + p->fts_link = root; + root = p; + } else { + p->fts_link = nullptr; + if (root == nullptr) { + tmp = root = p; + } else { + tmp->fts_link = p; + tmp = p; + } + } + } + if (compar && nitems > 1) { + root = fts_sort(sp, root, nitems); + } + + /* + * Allocate a dummy pointer and make yfts_read think that we've just + * finished the node before the root(s); set p->fts_info to FTS_INIT + * so that everything about the "current" node is ignored. + */ + if ((sp->fts_cur = fts_alloc(sp, "", 0)) == nullptr) { + goto mem3; + } + sp->fts_cur->fts_level = FTS_ROOTLEVEL; + sp->fts_cur->fts_link = root; + sp->fts_cur->fts_info = FTS_INIT; + + /* + * If using chdir(2), grab a file descriptor pointing to dot to ensure + * that we can get back here; this could be avoided for some paths, + * but almost certainly not worth the effort. Slashes, symbolic links, + * and ".." are all fairly nasty problems. Note, if we can't get the + * descriptor we run anyway, just more slowly. + */ + + if (!ISSET(FTS_NOCHDIR) && valid_dird(sp->fts_rfd = get_cwdd())) { + SET(FTS_NOCHDIR); + } + + return (sp); + +mem3: + fts_lfree(root); + free(parent); +mem2: + free(sp->fts_path); +mem1: + free(sp); + return nullptr; +} + +static void +fts_load(FTS* sp, FTSENT* p) +{ + size_t len; + char* cp; + + /* + * Load the stream structure for the next traversal. Since we don't + * actually enter the directory until after the preorder visit, set + * the fts_accpath field specially so the chdir gets done to the right + * place and the user can access the first node. From yfts_open it's + * known that the path will fit. + */ + len = p->fts_pathlen = p->fts_namelen; + memmove((void*)sp->fts_path, (void*)p->fts_name, len + 1); + if ((cp = strrchr(p->fts_name, LOCSLASH_C)) != nullptr && (cp != p->fts_name || cp[1])) { + len = strlen(++cp); + memmove((void*)p->fts_name, (void*)cp, len + 1); + p->fts_namelen = (u_short)len; + } + p->fts_accpath = p->fts_path = sp->fts_path; + sp->fts_dev = p->fts_dev; +} + +int yfts_close(FTS* sp) +{ + FTSENT *freep, *p; + int saved_errno; + + /* + * This still works if we haven't read anything -- the dummy structure + * points to the root list, so we step through to the end of the root + * list which has a valid parent pointer. + */ + if (sp->fts_cur) { + for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { + freep = p; + p = p->fts_link ? p->fts_link : p->fts_parent; + free(freep); + } + free(p); + } + + /* Free up child linked list, sort array, path buffer. */ + if (sp->fts_child) { + fts_lfree(sp->fts_child); + } + if (sp->fts_array) { + free(sp->fts_array); + } + free(sp->fts_path); + + /* Return to original directory, save errno if necessary. */ + if (!ISSET(FTS_NOCHDIR)) { + saved_errno = chdir_dird(sp->fts_rfd) ? errno : 0; + close_dird(sp->fts_rfd); + + /* Set errno and return. */ + if (saved_errno != 0) { + /* Free up the stream pointer. */ + free(sp); + errno = saved_errno; + return (-1); + } + } + + /* Free up the stream pointer. */ + free(sp); + return (0); +} + +/* + * Special case of "/" at the end of the path so that slashes aren't + * appended which would cause paths to be written as "....//foo". + */ +#define NAPPEND(p) \ + (p->fts_path[p->fts_pathlen - 1] == LOCSLASH_C \ + ? p->fts_pathlen - 1 \ + : p->fts_pathlen) + +FTSENT* +yfts_read(FTS* sp) { + FTSENT *p, *tmp; + int instr; + char* t; + int saved_errno; + + ClearLastSystemError(); + + /* If finished or unrecoverable error, return NULL. */ + if (sp->fts_cur == nullptr || ISSET(FTS_STOP)) { + return nullptr; + } + + /* Set current node pointer. */ + p = sp->fts_cur; + + /* Save and zero out user instructions. */ + instr = p->fts_instr; + p->fts_instr = FTS_NOINSTR; + + /* Any type of file may be re-visited; re-stat and re-turn. */ + if (instr == FTS_AGAIN) { + p->fts_info = fts_stat(sp, p, 0); + p->fts_type = yfts_type_from_info(p->fts_info); + return (p); + } + + /* + * Following a symlink -- SLNONE test allows application to see + * SLNONE and recover. If indirecting through a symlink, have + * keep a pointer to current location. If unable to get that + * pointer, follow fails. + */ + if (instr == FTS_FOLLOW && + (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { + p->fts_info = fts_stat(sp, p, 1); + p->fts_type = yfts_type_from_info(p->fts_info); + if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { + if (valid_dird(p->fts_symfd = get_cwdd())) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + } else { + p->fts_flags |= FTS_SYMFOLLOW; + } + } + return (p); + } + + /* Directory in pre-order. */ + if (p->fts_info == FTS_D) { + /* If skipped or crossed mount point, do post-order visit. */ + if (instr == FTS_SKIP || + (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { + if (p->fts_flags & FTS_SYMFOLLOW) { + close_dird(p->fts_symfd); + } + if (sp->fts_child) { + fts_lfree(sp->fts_child); + sp->fts_child = nullptr; + } + p->fts_info = FTS_DP; + return (p); + } + + /* Rebuild if only read the names and now traversing. */ + if (sp->fts_child && ISSET(FTS_NAMEONLY)) { + CLR(FTS_NAMEONLY); + fts_lfree(sp->fts_child); + sp->fts_child = nullptr; + } + + /* + * Cd to the subdirectory. + * + * If have already read and now fail to chdir, whack the list + * to make the names come out right, and set the parent errno + * so the application will eventually get an error condition. + * Set the FTS_DONTCHDIR flag so that when we logically change + * directories back to the parent we don't do a chdir. + * + * If haven't read do so. If the read fails, fts_build sets + * FTS_STOP or the fts_info field of the node. + */ + if (sp->fts_child) { + if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { + p->fts_errno = errno; + p->fts_flags |= FTS_DONTCHDIR; + for (p = sp->fts_child; p; p = p->fts_link) { + p->fts_accpath = + p->fts_parent->fts_accpath; + } + } + } else if ((sp->fts_child = fts_build(sp, BREAD)) == nullptr) { + if (ISSET(FTS_STOP)) { + return nullptr; + } + return (p); + } + p = sp->fts_child; + sp->fts_child = nullptr; + goto name; + } + + /* Move to the next node on this level. */ +next: + tmp = p; + if ((p = p->fts_link) != nullptr) { + free(tmp); + + /* + * If reached the top, return to the original directory (or + * the root of the tree), and load the paths for the next root. + */ + if (p->fts_level == FTS_ROOTLEVEL) { + if (FCHDIR(sp, sp->fts_rfd)) { + SET(FTS_STOP); + return nullptr; + } + fts_load(sp, p); + return (sp->fts_cur = p); + } + + /* + * User may have called yfts_set on the node. If skipped, + * ignore. If followed, get a file descriptor so we can + * get back if necessary. + */ + if (p->fts_instr == FTS_SKIP) { + goto next; + } + if (p->fts_instr == FTS_FOLLOW) { + p->fts_info = fts_stat(sp, p, 1); + p->fts_type = yfts_type_from_info(p->fts_info); + if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { + if (valid_dird(p->fts_symfd = + get_cwdd())) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + } else { + p->fts_flags |= FTS_SYMFOLLOW; + } + } + p->fts_instr = FTS_NOINSTR; + } + + name: + t = sp->fts_path + NAPPEND(p->fts_parent); + *t++ = LOCSLASH_C; + memmove(t, p->fts_name, (size_t)p->fts_namelen + 1); + return (sp->fts_cur = p); + } + + /* Move up to the parent node. */ + p = tmp->fts_parent; + free(tmp); + + if (p->fts_level == FTS_ROOTPARENTLEVEL) { + /* + * Done; free everything up and set errno to 0 so the user + * can distinguish between error and EOF. + */ + free(p); + errno = 0; + return (sp->fts_cur = nullptr); + } + + /* NUL terminate the pathname. */ + sp->fts_path[p->fts_pathlen] = '\0'; + + /* + * Return to the parent directory. If at a root node or came through + * a symlink, go back through the file descriptor. Otherwise, cd up + * one directory. + */ + if (p->fts_level == FTS_ROOTLEVEL) { + if (FCHDIR(sp, sp->fts_rfd)) { + SET(FTS_STOP); + return nullptr; + } + } else if (p->fts_flags & FTS_SYMFOLLOW) { + if (FCHDIR(sp, p->fts_symfd)) { + saved_errno = errno; + close_dird(p->fts_symfd); + errno = saved_errno; + SET(FTS_STOP); + return nullptr; + } + close_dird(p->fts_symfd); + } else if (!(p->fts_flags & FTS_DONTCHDIR) && + fts_safe_changedir(sp, p->fts_parent, -1, "..")) { + SET(FTS_STOP); + return nullptr; + } + p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; + return (sp->fts_cur = p); +} + +/* + * Fts_set takes the stream as an argument although it's not used in this + * implementation; it would be necessary if anyone wanted to add global + * semantics to fts using yfts_set. An error return is allowed for similar + * reasons. + */ +/* ARGSUSED */ +int yfts_set(FTS* sp, FTSENT* p, int instr) +{ + (void)sp; //Unused + if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && + instr != FTS_NOINSTR && instr != FTS_SKIP) { + errno = EINVAL; + return (1); + } + p->fts_instr = (u_short)instr; + return (0); +} + +FTSENT* +yfts_children(FTS* sp, int instr) +{ + FTSENT* p; + dird fd; + if (instr && instr != FTS_NAMEONLY) { + errno = EINVAL; + return nullptr; + } + + /* Set current node pointer. */ + p = sp->fts_cur; + + /* + * Errno set to 0 so user can distinguish empty directory from + * an error. + */ + errno = 0; + + /* Fatal errors stop here. */ + if (ISSET(FTS_STOP)) { + return nullptr; + } + + /* Return logical hierarchy of user's arguments. */ + if (p->fts_info == FTS_INIT) { + return (p->fts_link); + } + + /* + * If not a directory being visited in pre-order, stop here. Could + * allow FTS_DNR, assuming the user has fixed the problem, but the + * same effect is available with FTS_AGAIN. + */ + if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) { + return nullptr; + } + + /* Free up any previous child list. */ + if (sp->fts_child) { + fts_lfree(sp->fts_child); + } + + if (instr == FTS_NAMEONLY) { + SET(FTS_NAMEONLY); + instr = BNAMES; + } else { + instr = BCHILD; + } + + /* + * If using chdir on a relative path and called BEFORE yfts_read does + * its chdir to the root of a traversal, we can lose -- we need to + * chdir into the subdirectory, and we don't know where the current + * directory is, so we can't get back so that the upcoming chdir by + * yfts_read will work. + */ + if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == LOCSLASH_C || + ISSET(FTS_NOCHDIR)) { + return (sp->fts_child = fts_build(sp, instr)); + } + + if (valid_dird(fd = get_cwdd())) { + return nullptr; + } + sp->fts_child = fts_build(sp, instr); + if (chdir_dird(fd)) { + close_dird(fd); + return nullptr; + } + close_dird(fd); + return (sp->fts_child); +} + +static inline struct dirent* yreaddir(DIR* dir, struct dirent* de) { + // TODO(yazevnul|IGNIETFERRO-1070): remove these macroses by replacing `readdir_r` with proper + // alternative + Y_PRAGMA_DIAGNOSTIC_PUSH + Y_PRAGMA_NO_DEPRECATED + if (readdir_r(dir, de, &de) == 0) { + Y_PRAGMA_DIAGNOSTIC_POP + return de; + } + + return nullptr; +} + +/* + * This is the tricky part -- do not casually change *anything* in here. The + * idea is to build the linked list of entries that are used by yfts_children + * and yfts_read. There are lots of special cases. + * + * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is + * set and it's a physical walk (so that symbolic links can't be directories), + * we can do things quickly. First, if it's a 4.4BSD file system, the type + * of the file is in the directory entry. Otherwise, we assume that the number + * of subdirectories in a node is equal to the number of links to the parent. + * The former skips all stat calls. The latter skips stat calls in any leaf + * directories and for any files after the subdirectories in the directory have + * been found, cutting the stat calls by about 2/3. + */ +static FTSENT* +fts_build(FTS* sp, int type) +{ + struct dirent* dp; + FTSENT *p, *head; + int nitems; + FTSENT *cur, *tail; + +#ifdef _win_ + dird dirpd; + struct DIR* dirp; +#else + DIR* dirp; +#endif + + void* oldaddr; + int cderrno, descend, len, level, maxlen, nlinks, saved_errno, + nostat, doadjust; + char* cp; + + /* Set current node pointer. */ + cur = sp->fts_cur; + + /* + * Open the directory for reading. If this fails, we're done. + * If being called from yfts_read, set the fts_info field. + */ +#ifdef FTS_WHITEOUT + if (ISSET(FTS_WHITEOUT)) + oflag = DTF_NODUP | DTF_REWIND; + else + oflag = DTF_HIDEW | DTF_NODUP | DTF_REWIND; +#else + #define __opendir2(path, flag) opendir(path) +#endif + if ((dirp = __opendir2(cur->fts_accpath, oflag)) == nullptr) { + if (type == BREAD) { + cur->fts_info = FTS_DNR; + cur->fts_errno = errno; + } + return nullptr; + } + +#ifdef _win_ + dirpd = get_dird(cur->fts_accpath); +#endif + + /* + * Nlinks is the number of possible entries of type directory in the + * directory if we're cheating on stat calls, 0 if we're not doing + * any stat calls at all, -1 if we're doing stats on everything. + */ + if (type == BNAMES) { + nlinks = 0; + /* Be quiet about nostat, GCC. */ + nostat = 0; + } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { + nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); + nostat = 1; + } else { + nlinks = -1; + nostat = 0; + } + + /* + * If we're going to need to stat anything or we want to descend + * and stay in the directory, chdir. If this fails we keep going, + * but set a flag so we don't chdir after the post-order visit. + * We won't be able to stat anything, but we can still return the + * names themselves. Note, that since yfts_read won't be able to + * chdir into the directory, it will have to return different path + * names than before, i.e. "a/b" instead of "b". Since the node + * has already been visited in pre-order, have to wait until the + * post-order visit to return the error. There is a special case + * here, if there was nothing to stat then it's not an error to + * not be able to stat. This is all fairly nasty. If a program + * needed sorted entries or stat information, they had better be + * checking FTS_NS on the returned nodes. + */ + cderrno = 0; + if (nlinks || type == BREAD) { +#ifndef _win_ + if (fts_safe_changedir(sp, cur, dirfd(dirp), nullptr)) { +#else + if (fts_safe_changedir(sp, cur, -1, dirpd)) { +#endif + + if (nlinks && type == BREAD) { + cur->fts_errno = errno; + } + cur->fts_flags |= FTS_DONTCHDIR; + descend = 0; + cderrno = errno; + (void)closedir(dirp); + dirp = nullptr; +#ifdef _win_ + close_dird(dirpd); + dirpd = invalidDirD; +#else + Y_UNUSED(invalidDirD); +#endif + } else { + descend = 1; + } + } else { + descend = 0; + } + + /* + * Figure out the max file name length that can be stored in the + * current path -- the inner loop allocates more path as necessary. + * We really wouldn't have to do the maxlen calculations here, we + * could do them in yfts_read before returning the path, but it's a + * lot easier here since the length is part of the dirent structure. + * + * If not changing directories set a pointer so that can just append + * each new name into the path. + */ + len = NAPPEND(cur); + if (ISSET(FTS_NOCHDIR)) { + cp = sp->fts_path + len; + *cp++ = LOCSLASH_C; + } else { + /* GCC, you're too verbose. */ + cp = nullptr; + } + ++len; + maxlen = sp->fts_pathlen - len; + + level = cur->fts_level + 1; + + /* Read the directory, attaching each entry to the `link' pointer. */ + doadjust = 0; + + //to ensure enough buffer + TTempBuf dpe; + + for (head = tail = nullptr, nitems = 0; dirp && (dp = yreaddir(dirp, (struct dirent*)dpe.Data())) != nullptr;) { + if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) { + continue; + } + + if ((p = fts_alloc(sp, dp->d_name, (int)strlen(dp->d_name))) == nullptr) { + goto mem1; + } + if (strlen(dp->d_name) >= (size_t)maxlen) { /* include space for NUL */ + oldaddr = sp->fts_path; + if (fts_palloc(sp, strlen(dp->d_name) + len + 1)) { + /* + * No more memory for path or structures. Save + * errno, free up the current structure and the + * structures already allocated. + */ + mem1: + saved_errno = errno; + if (p) { + free(p); + } + fts_lfree(head); + (void)closedir(dirp); +#ifdef _win_ + close_dird(dirpd); +#endif + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + errno = saved_errno; + return nullptr; + } + /* Did realloc() change the pointer? */ + if (oldaddr != sp->fts_path) { + doadjust = 1; + if (ISSET(FTS_NOCHDIR)) { + cp = sp->fts_path + len; + } + } + maxlen = sp->fts_pathlen - len; + } + + if (len + strlen(dp->d_name) >= USHRT_MAX) { + /* + * In an FTSENT, fts_pathlen is a u_short so it is + * possible to wraparound here. If we do, free up + * the current structure and the structures already + * allocated, then error out with ENAMETOOLONG. + */ + free(p); + fts_lfree(head); + (void)closedir(dirp); +#ifdef _win_ + close_dird(dirpd); +#endif + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + errno = ENAMETOOLONG; + return nullptr; + } + p->fts_level = (short)level; + p->fts_parent = sp->fts_cur; + p->fts_pathlen = u_short(len + strlen(dp->d_name)); + +#ifdef FTS_WHITEOUT + if (dp->d_type == DT_WHT) + p->fts_flags |= FTS_ISW; +#endif + +#ifdef _DIRENT_HAVE_D_TYPE + if (dp->d_type == DT_DIR) { + p->fts_type = FTS_D; + } else if (dp->d_type == DT_REG) { + p->fts_type = FTS_F; + } else if (dp->d_type == DT_LNK) { + p->fts_type = FTS_SL; + } +#endif + + // coverity[dead_error_line]: false positive + if (cderrno) { + if (nlinks) { + p->fts_info = FTS_NS; + p->fts_errno = cderrno; + } else { + p->fts_info = FTS_NSOK; + } + p->fts_accpath = cur->fts_accpath; + } else if (nlinks == 0 +#ifdef DT_DIR + || (nostat && + dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) +#endif + ) { + p->fts_accpath = + ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; + p->fts_info = FTS_NSOK; + } else { + /* Build a file name for fts_stat to stat. */ + if (ISSET(FTS_NOCHDIR)) { + p->fts_accpath = p->fts_path; + memmove((void*)cp, (void*)p->fts_name, (size_t)p->fts_namelen + 1); + } else { + p->fts_accpath = p->fts_name; + } + /* Stat it. */ + p->fts_info = fts_stat(sp, p, 0); + p->fts_type = yfts_type_from_info(p->fts_info); + + /* Decrement link count if applicable. */ + if (nlinks > 0 && (p->fts_info == FTS_D || + p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) { + --nlinks; + } + } + + /* We walk in directory order so "ls -f" doesn't get upset. */ + p->fts_link = nullptr; + if (head == nullptr) { + head = tail = p; + } else { + tail->fts_link = p; + tail = p; + } + ++nitems; + } + if (dirp) { + (void)closedir(dirp); +#ifdef _win_ + close_dird(dirpd); +#endif + } + + /* + * If realloc() changed the address of the path, adjust the + * addresses for the rest of the tree and the dir list. + */ + if (doadjust) { + fts_padjust(sp); + } + + /* + * If not changing directories, reset the path back to original + * state. + */ + if (ISSET(FTS_NOCHDIR)) { + if (len == sp->fts_pathlen || nitems == 0) { + --cp; + } + *cp = '\0'; + } + + /* + * If descended after called from yfts_children or after called from + * yfts_read and nothing found, get back. At the root level we use + * the saved fd; if one of yfts_open()'s arguments is a relative path + * to an empty directory, we wind up here with no other way back. If + * can't get back, we're done. + */ + if (descend && (type == BCHILD || !nitems) && + (cur->fts_level == FTS_ROOTLEVEL ? FCHDIR(sp, sp->fts_rfd) : fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + fts_lfree(head); + return nullptr; + } + + /* If didn't find anything, return NULL. */ + if (!nitems) { + if (type == BREAD) { + cur->fts_info = FTS_DP; + } + fts_lfree(head); + return nullptr; + } + + /* Sort the entries. */ + if (sp->fts_compar && nitems > 1) { + head = fts_sort(sp, head, nitems); + } + return (head); +} + +static u_short +fts_stat(FTS* sp, FTSENT* p, int follow) +{ + dev_t dev; + ino_t ino; + stat_struct *sbp, sb; + int saved_errno; + /* If user needs stat info, stat buffer already allocated. */ + sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; + +#ifdef FTS_WHITEOUT + /* check for whiteout */ + if (p->fts_flags & FTS_ISW) { + if (sbp != &sb) { + memset(sbp, '\0', sizeof(*sbp)); + sbp->st_mode = S_IFWHT; + } + return (FTS_W); + } +#endif + + /* + * If doing a logical walk, or application requested FTS_FOLLOW, do + * a stat(2). If that fails, check for a non-existent symlink. If + * fail, set the errno from the stat call. + */ + if (ISSET(FTS_LOGICAL) || follow) { + if (STAT_FUNC(p->fts_accpath, sbp)) { + saved_errno = errno; + if (!lstat(p->fts_accpath, sbp)) { + errno = 0; + return (FTS_SLNONE); + } + p->fts_errno = saved_errno; + memset(sbp, 0, sizeof(stat_struct)); + return (FTS_NS); + } + } else if (lstat(p->fts_accpath, sbp)) { + p->fts_errno = errno; + memset(sbp, 0, sizeof(stat_struct)); + return (FTS_NS); + } + + if (S_ISDIR(sbp->st_mode)) { + /* + * Set the device/inode. Used to find cycles and check for + * crossing mount points. Also remember the link count, used + * in fts_build to limit the number of stat calls. It is + * understood that these fields are only referenced if fts_info + * is set to FTS_D. + */ + dev = p->fts_dev = sbp->st_dev; + ino = p->fts_ino = sbp->st_ino; + p->fts_nlink = sbp->st_nlink; + + const char* fts_name_x = p->fts_name; + if (ISDOT(fts_name_x)) { + return (FTS_DOT); + } + + /* + * Cycle detection is done by brute force when the directory + * is first encountered. If the tree gets deep enough or the + * number of symbolic links to directories is high enough, + * something faster might be worthwhile. + */ + + //There is no way to detect symlink or mount cycles on win32 + +#ifndef _win_ + FTSENT* t; + for (t = p->fts_parent; + t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) { + if (ino == t->fts_ino && dev == t->fts_dev) { + p->fts_cycle = t; + return (FTS_DC); + } + } +#endif /*_win_*/ + return (FTS_D); + } + if (S_ISLNK(sbp->st_mode)) { + return (FTS_SL); + } + if (S_ISREG(sbp->st_mode)) { + return (FTS_F); + } + return (FTS_DEFAULT); +} + +static FTSENT* +fts_sort(FTS* sp, FTSENT* head, int nitems) +{ + FTSENT **ap, *p; + + /* + * Construct an array of pointers to the structures and call qsort(3). + * Reassemble the array in the order returned by qsort. If unable to + * sort for memory reasons, return the directory entries in their + * current order. Allocate enough space for the current needs plus + * 40 so don't realloc one entry at a time. + */ + if (nitems > sp->fts_nitems) { + struct _ftsent** a; + + sp->fts_nitems = nitems + 40; + if ((a = (struct _ftsent**)realloc(sp->fts_array, + sp->fts_nitems * sizeof(FTSENT*))) == nullptr) { + if (sp->fts_array) { + free(sp->fts_array); + } + sp->fts_array = nullptr; + sp->fts_nitems = 0; + return (head); + } + sp->fts_array = a; + } + for (ap = sp->fts_array, p = head; p; p = p->fts_link) { + *ap++ = p; + } + qsort((void*)sp->fts_array, (size_t)nitems, sizeof(FTSENT*), (int (*)(const void*, const void*))sp->fts_compar); + for (head = *(ap = sp->fts_array); --nitems; ++ap) { + ap[0]->fts_link = ap[1]; + } + ap[0]->fts_link = nullptr; + return (head); +} + +static FTSENT* +fts_alloc(FTS* sp, const char* name, int namelen) +{ + FTSENT* p; + size_t len; + + /* + * The file name is a variable length array and no stat structure is + * necessary if the user has set the nostat bit. Allocate the FTSENT + * structure, the file name and the stat structure in one chunk, but + * be careful that the stat structure is reasonably aligned. Since the + * fts_name field is declared to be of size 1, the fts_name pointer is + * namelen + 2 before the first possible address of the stat structure. + */ + len = sizeof(FTSENT) + namelen; + if (!ISSET(FTS_NOSTAT)) { + len += sizeof(stat_struct) + ALIGNBYTES; + } + if ((p = (FTSENT*)malloc(len)) == nullptr) { + return nullptr; + } + + /* Copy the name and guarantee NUL termination. */ + memmove((void*)p->fts_name, (void*)name, (size_t)namelen); + p->fts_name[namelen] = '\0'; + + if (!ISSET(FTS_NOSTAT)) { + p->fts_statp = (stat_struct*)ALIGN(p->fts_name + namelen + 2); + } else { + p->fts_statp = nullptr; + } + p->fts_namelen = (u_short)namelen; + p->fts_path = sp->fts_path; + p->fts_errno = 0; + p->fts_flags = 0; + p->fts_instr = FTS_NOINSTR; + p->fts_number = 0; + p->fts_pointer = nullptr; + p->fts_type = FTS_NSOK; + return (p); +} + +static void +fts_lfree(FTSENT* head) +{ + FTSENT* p; + + /* Free a linked list of structures. */ + while ((p = head) != nullptr) { + head = head->fts_link; + free(p); + } +} + +/* + * Allow essentially unlimited paths; find, rm, ls should all work on any tree. + * Most systems will allow creation of paths much longer than MAXPATHLEN, even + * though the kernel won't resolve them. Add the size (not just what's needed) + * plus 256 bytes so don't realloc the path 2 bytes at a time. + */ +static int +fts_palloc(FTS* sp, size_t more) +{ + sp->fts_pathlen += more + 256; + sp->fts_path = (char*)yreallocf(sp->fts_path, (size_t)sp->fts_pathlen); + return (sp->fts_path == nullptr); +} + +static void +ADJUST(FTSENT* p, void* addr) +{ + if ((p)->fts_accpath >= (p)->fts_path && + (p)->fts_accpath < (p)->fts_path + (p)->fts_pathlen) { + if (p->fts_accpath != p->fts_path) { + errx(1, "fts ADJUST: accpath %p path %p", + p->fts_accpath, p->fts_path); + } + if (p->fts_level != 0) { + errx(1, "fts ADJUST: level %d not 0", p->fts_level); + } + (p)->fts_accpath = + (char*)addr + ((p)->fts_accpath - (p)->fts_path); + } + (p)->fts_path = (char*)addr; +} + +/* + * When the path is realloc'd, have to fix all of the pointers in structures + * already returned. + */ +static void +fts_padjust(FTS* sp) +{ + FTSENT* p; + char* addr = sp->fts_path; + +#define ADJUST1(p) \ + { \ + if ((p)->fts_accpath == (p)->fts_path) \ + (p)->fts_accpath = (addr); \ + (p)->fts_path = addr; \ + } + /* Adjust the current set of children. */ + for (p = sp->fts_child; p; p = p->fts_link) { + ADJUST(p, addr); + } + + /* Adjust the rest of the tree. */ + for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { + ADJUST(p, addr); + p = p->fts_link ? p->fts_link : p->fts_parent; + } +} + +static size_t +fts_maxarglen(char* const* argv) +{ + size_t len, max; + + for (max = 0; *argv; ++argv) { + if ((len = strlen(*argv)) > max) { + max = len; + } + } + return (max + 1); +} + +/* + * Change to dir specified by fd or p->fts_accpath without getting + * tricked by someone changing the world out from underneath us. + * Assumes p->fts_dev and p->fts_ino are filled in. + */ + +#ifndef _win_ +static int +fts_safe_changedir(FTS* sp, FTSENT* p, int fd, const char* path) +{ + int ret, oerrno, newfd; + stat_struct sb; + + newfd = fd; + if (ISSET(FTS_NOCHDIR)) { + return (0); + } + if (fd < 0 && (newfd = open(path, O_RDONLY, 0)) < 0) { + return (-1); + } + if (fstat(newfd, &sb)) { + ret = -1; + goto bail; + } + if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) { + errno = ENOENT; /* disinformation */ + ret = -1; + goto bail; + } + ret = fchdir(newfd); +bail: + oerrno = errno; + if (fd < 0) { + (void)close(newfd); + } + errno = oerrno; + return (ret); +} +#else +static int +fts_safe_changedir(FTS* sp, FTSENT* p, int /*fd*/, const char* path) +{ + int ret; + stat_struct sb; + + if (ISSET(FTS_NOCHDIR)) + return (0); + if (STAT_FUNC(path, &sb)) { + ret = -1; + goto bail; + } + if (p->fts_dev != sb.st_dev) { + errno = ENOENT; /* disinformation */ + ret = -1; + goto bail; + } + ret = chdir_dird(path); +bail: + return (ret); +} + +static int +fts_safe_changedir(FTS* sp, FTSENT* p, int /*fd*/, dird path) { + int ret; + stat_struct sb; + + if (ISSET(FTS_NOCHDIR)) + return (0); + if (STAT_FUNC(path, &sb)) { + ret = -1; + goto bail; + } + if (p->fts_dev != sb.st_dev) { + errno = ENOENT; /* disinformation */ + ret = -1; + goto bail; + } + ret = chdir_dird(path); +bail: + return (ret); +} +#endif diff --git a/util/folder/fts.h b/util/folder/fts.h new file mode 100644 index 00000000000..f3c799e8c88 --- /dev/null +++ b/util/folder/fts.h @@ -0,0 +1,108 @@ +#pragma once + +#include + +#include + +#ifndef _win32_ +typedef int dird; +typedef struct stat stat_struct; + #define STAT_FUNC stat +#else + #include +typedef WCHAR* dird; +typedef unsigned short u_short; +typedef unsigned int nlink_t; +typedef struct _stat64 stat_struct; + #define STAT_FUNC stat64UTF + //TODO: remove from global scope stat64UTF stat64UTF + #ifdef __cplusplus +int stat64UTF(const char* path, struct _stat64* _Stat); +int stat64UTF(dird path, struct _stat64* _Stat); + #endif +#endif + +typedef struct { + struct _ftsent* fts_cur; /* current node */ + struct _ftsent* fts_child; /* linked list of children */ + struct _ftsent** fts_array; /* sort array */ + dev_t fts_dev; /* starting device # */ + char* fts_path; /* path for this descent */ + dird fts_rfd; /* fd for root */ + int fts_pathlen; /* sizeof(path) */ + int fts_nitems; /* elements in the sort array */ + int(*fts_compar) /* compare function */ + (const struct _ftsent**, const struct _ftsent**); + +#define FTS_COMFOLLOW 0x001 /* follow command line symlinks */ +#define FTS_LOGICAL 0x002 /* logical walk */ +#define FTS_NOCHDIR 0x004 /* don't change directories */ +#define FTS_NOSTAT 0x008 /* don't get stat info */ +#define FTS_PHYSICAL 0x010 /* physical walk */ +#define FTS_SEEDOT 0x020 /* return dot and dot-dot */ +#define FTS_XDEV 0x040 /* don't cross devices */ +#define FTS_OPTIONMASK 0x0ff /* valid user option mask */ + +#define FTS_NAMEONLY 0x100 /* (private) child names only */ +#define FTS_STOP 0x200 /* (private) unrecoverable error */ + int fts_options; /* yfts_open options, global flags */ +} FTS; + +typedef struct _ftsent { + struct _ftsent* fts_cycle; /* cycle node */ + struct _ftsent* fts_parent; /* parent directory */ + struct _ftsent* fts_link; /* next file in directory */ + long fts_number; /* local numeric value */ + void* fts_pointer; /* local address value */ + char* fts_accpath; /* access path */ + char* fts_path; /* root path */ + int fts_errno; /* errno for this node */ + dird fts_symfd; /* fd for symlink */ + u_short fts_pathlen; /* strlen(fts_path) */ + u_short fts_namelen; /* strlen(fts_name) */ + + ino_t fts_ino; /* inode */ + dev_t fts_dev; /* device */ + nlink_t fts_nlink; /* link count */ + +#define FTS_ROOTPARENTLEVEL -1 +#define FTS_ROOTLEVEL 0 + short fts_level; /* depth (-1 to N) */ + +#define FTS_D 1 /* preorder directory */ +#define FTS_DC 2 /* directory that causes cycles */ +#define FTS_DEFAULT 3 /* none of the above */ +#define FTS_DNR 4 /* unreadable directory */ +#define FTS_DOT 5 /* dot or dot-dot */ +#define FTS_DP 6 /* postorder directory */ +#define FTS_ERR 7 /* error; errno is set */ +#define FTS_F 8 /* regular file */ +#define FTS_INIT 9 /* initialized only */ +#define FTS_NS 10 /* stat(2) failed */ +#define FTS_NSOK 11 /* no stat(2) requested */ +#define FTS_SL 12 /* symbolic link */ +#define FTS_SLNONE 13 /* symbolic link without target */ +#define FTS_W 14 /* whiteout object */ + u_short fts_info; /* user flags for FTSENT structure */ + u_short fts_type; /* type of fs node; one of FTS_D, FTS_F, FTS_SL */ + +#define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */ +#define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here */ +#define FTS_ISW 0x04 /* this is a whiteout object */ + u_short fts_flags; /* private flags for FTSENT structure */ + +#define FTS_AGAIN 1 /* read node again */ +#define FTS_FOLLOW 2 /* follow symbolic link */ +#define FTS_NOINSTR 3 /* no instructions */ +#define FTS_SKIP 4 /* discard node */ + u_short fts_instr; /* yfts_set() instructions */ + + stat_struct* fts_statp; /* stat(2) information */ + char fts_name[1]; /* file name */ +} FTSENT; + +FTSENT* yfts_children(FTS*, int); +int yfts_close(FTS*); +FTS* yfts_open(char* const*, int, int (*)(const FTSENT**, const FTSENT**)); +FTSENT* yfts_read(FTS*); +int yfts_set(FTS*, FTSENT*, int); diff --git a/util/folder/fts_ut.cpp b/util/folder/fts_ut.cpp new file mode 100644 index 00000000000..c5d59e35f42 --- /dev/null +++ b/util/folder/fts_ut.cpp @@ -0,0 +1,123 @@ +#include "fts.h" +#include "dirut.h" +#include "tempdir.h" + +#include +#include + +#include +#include +#include + +class TFtsTest: public TTestBase { + UNIT_TEST_SUITE(TFtsTest); + UNIT_TEST(TestSimple); + UNIT_TEST(TestNoLeakChangingAccessToFolder); + UNIT_TEST_SUITE_END(); + +public: + void TestSimple(); + void TestNoLeakChangingAccessToFolder(); +}; + +void MakeFile(const char* path) { + TFile(path, CreateAlways); +} + +//There potentially could be problems in listing order on different platforms +int FtsCmp(const FTSENT** ent1, const FTSENT** ent2) { + return strcmp((*ent1)->fts_accpath, (*ent2)->fts_accpath); +} + +void CheckEnt(FTSENT* ent, const char* name, int type) { + UNIT_ASSERT(ent); + UNIT_ASSERT_STRINGS_EQUAL(ent->fts_path, name); + UNIT_ASSERT_EQUAL(ent->fts_info, type); +} + +class TFileTree { +public: + TFileTree(char* const* argv, int options, int (*compar)(const FTSENT**, const FTSENT**)) { + Fts_ = yfts_open(argv, options, compar); + } + + ~TFileTree() { + yfts_close(Fts_); + } + + FTS* operator()() { + return Fts_; + } + +private: + FTS* Fts_; +}; + +void TFtsTest::TestSimple() { + const char* dotPath[2] = {"." LOCSLASH_S, nullptr}; + TFileTree currentDirTree((char* const*)dotPath, 0, FtsCmp); + UNIT_ASSERT(currentDirTree()); + TTempDir tempDir = MakeTempName(yfts_read(currentDirTree())->fts_path); + MakeDirIfNotExist(tempDir().data()); + MakeDirIfNotExist((tempDir() + LOCSLASH_S "dir1").data()); + MakeFile((tempDir() + LOCSLASH_S "dir1" LOCSLASH_S "file1").data()); + MakeFile((tempDir() + LOCSLASH_S "dir1" LOCSLASH_S "file2").data()); + MakeDirIfNotExist((tempDir() + LOCSLASH_S "dir2").data()); + MakeFile((tempDir() + LOCSLASH_S "dir2" LOCSLASH_S "file3").data()); + MakeFile((tempDir() + LOCSLASH_S "dir2" LOCSLASH_S "file4").data()); + + const char* path[2] = {tempDir().data(), nullptr}; + TFileTree fileTree((char* const*)path, 0, FtsCmp); + UNIT_ASSERT(fileTree()); + CheckEnt(yfts_read(fileTree()), tempDir().data(), FTS_D); + CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir1").data(), FTS_D); + CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir1" LOCSLASH_S "file1").data(), FTS_F); + CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir1" LOCSLASH_S "file2").data(), FTS_F); + CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir1").data(), FTS_DP); + CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir2").data(), FTS_D); + CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir2" LOCSLASH_S "file3").data(), FTS_F); + CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir2" LOCSLASH_S "file4").data(), FTS_F); + CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir2").data(), FTS_DP); + CheckEnt(yfts_read(fileTree()), (tempDir()).data(), FTS_DP); + UNIT_ASSERT_EQUAL(yfts_read(fileTree()), nullptr); +} + +class TTempDirWithLostAccess: public TTempDir { +public: + ~TTempDirWithLostAccess() { + chmod(Name().data(), 0777); + } +}; + +// https://st.yandex-team.ru/YQ-318 +// Test that detects memory leak in case of error in chdir in fts_build function. +void TFtsTest::TestNoLeakChangingAccessToFolder() { + TTempDirWithLostAccess tempDir; + TString tmpPath = tempDir(); + if (tmpPath.EndsWith(LOCSLASH_S)) { + tmpPath.resize(tmpPath.size() - 1); + } + MakeDirIfNotExist((tmpPath + LOCSLASH_S + "subdir").data()); + + const char* path[2] = {tmpPath.data(), nullptr}; + TFileTree fileTree((char* const*)path, FTS_SEEDOT, FtsCmp); + UNIT_ASSERT(fileTree()); + + CheckEnt(yfts_read(fileTree()), tmpPath.data(), FTS_D); +#ifndef _win32_ + CheckEnt(yfts_read(fileTree()), (tmpPath + LOCSLASH_S ".").data(), FTS_DOT); +#endif // _win32_ + CheckEnt(yfts_read(fileTree()), (tmpPath + LOCSLASH_S "..").data(), FTS_DOT); + CheckEnt(yfts_read(fileTree()), (tmpPath + LOCSLASH_S "subdir").data(), FTS_D); + auto pool = CreateThreadPool(2); + auto chmodFuture = NThreading::Async([name = tmpPath] { + UNIT_ASSERT_C(!chmod(name.data(), 0), "Errno: " << errno); + }, *pool); + auto childrenFuture = NThreading::Async([&] { + yfts_children(fileTree(), 0); + }, *pool); + childrenFuture.Wait(); + chmodFuture.Wait(); +} + +UNIT_TEST_SUITE_REGISTRATION(TFtsTest); diff --git a/util/folder/fwd.cpp b/util/folder/fwd.cpp new file mode 100644 index 00000000000..4214b6df83e --- /dev/null +++ b/util/folder/fwd.cpp @@ -0,0 +1 @@ +#include "fwd.h" diff --git a/util/folder/fwd.h b/util/folder/fwd.h new file mode 100644 index 00000000000..8b1869a2d90 --- /dev/null +++ b/util/folder/fwd.h @@ -0,0 +1,5 @@ +#pragma once + +class TFsPath; + +class TTempDir; diff --git a/util/folder/iterator.cpp b/util/folder/iterator.cpp new file mode 100644 index 00000000000..73703d31f91 --- /dev/null +++ b/util/folder/iterator.cpp @@ -0,0 +1,11 @@ +#include "iterator.h" + +#include + +static int SortFTSENTByName(const FTSENT** a, const FTSENT** b) { + return strcmp((*a)->fts_name, (*b)->fts_name); +} + +TDirIterator::TOptions& TDirIterator::TOptions::SetSortByName() noexcept { + return SetSortFunctor(SortFTSENTByName); +} diff --git a/util/folder/iterator.h b/util/folder/iterator.h new file mode 100644 index 00000000000..c79d56c6543 --- /dev/null +++ b/util/folder/iterator.h @@ -0,0 +1,106 @@ +#pragma once + +#include "fts.h" + +#include +#include +#include +#include + +/// Note this magic API traverses directory hierarchy + +class TDirIterator: public TInputRangeAdaptor { + struct TFtsDestroy { + static inline void Destroy(FTS* f) noexcept { + yfts_close(f); + } + }; + +public: + class TError: public TSystemError { + public: + inline TError(int err) + : TSystemError(err) + { + } + }; + + using TCompare = int (*)(const FTSENT**, const FTSENT**); + + struct TOptions { + inline TOptions() { + Init(FTS_PHYSICAL); + } + + inline TOptions(int opts) { + Init(opts); + } + + inline TOptions& SetMaxLevel(size_t level) noexcept { + MaxLevel = level; + + return *this; + } + + inline TOptions& SetSortFunctor(TCompare cmp) noexcept { + Cmp = cmp; + + return *this; + } + + TOptions& SetSortByName() noexcept; + + int FtsOptions; + size_t MaxLevel; + TCompare Cmp; + + private: + inline void Init(int opts) noexcept { + FtsOptions = opts | FTS_NOCHDIR; + MaxLevel = Max(); + Cmp = nullptr; + } + }; + + inline TDirIterator(const TString& path, const TOptions& options = TOptions()) + : Options_(options) + , Path_(path) + { + Trees_[0] = Path_.begin(); + Trees_[1] = nullptr; + + FileTree_.Reset(yfts_open(Trees_, Options_.FtsOptions, Options_.Cmp)); + + if (!FileTree_.Get() || FileTree_->fts_cur->fts_link->fts_errno) { + ythrow TError(FileTree_.Get() ? FileTree_->fts_cur->fts_link->fts_errno : LastSystemError()) << "can not open '" << Path_ << "'"; + } + } + + inline FTSENT* Next() { + FTSENT* ret = yfts_read(FileTree_.Get()); + + if (ret) { + if ((size_t)(ret->fts_level + 1) > Options_.MaxLevel) { + yfts_set(FileTree_.Get(), ret, FTS_SKIP); + } + } else { + const int err = LastSystemError(); + + if (err) { + ythrow TError(err) << "error while iterating " << Path_; + } + } + + return ret; + } + + inline void Skip(FTSENT* ent) { + yfts_set(FileTree_.Get(), ent, FTS_SKIP); + } + +private: + TOptions Options_; + TString Path_; + char* Trees_[2]; + THolder FileTree_; +}; diff --git a/util/folder/iterator_ut.cpp b/util/folder/iterator_ut.cpp new file mode 100644 index 00000000000..c54cde55c81 --- /dev/null +++ b/util/folder/iterator_ut.cpp @@ -0,0 +1,218 @@ +#include "dirut.h" +#include "iterator.h" + +#include + +#include +#include +#include +#include +#include + +static TString JoinWithNewline(const TVector& strings) { + TStringStream ss; + for (const auto& string : strings) { + ss << string << "\n"; + } + return ss.Str(); +} + +class TDirIteratorTest: public TTestBase { + UNIT_TEST_SUITE(TDirIteratorTest); + UNIT_TEST(TestIt) + UNIT_TEST(TestError) + UNIT_TEST(TestLocal) + UNIT_TEST(TestSkip) + UNIT_TEST(TestSort) + UNIT_TEST_SUITE_END(); + +private: + class TDirHier { + public: + struct TPath { + TString Path; + int Type; + }; + + inline void AddFile(const TString& path) { + Add(path, 0); + } + + inline void AddDir(const TString& path) { + Add(path, 1); + } + + inline void Add(const TString& path, int type) { + const TPath p = { + path, type}; + + Add(p); + } + + inline void Add(const TPath& path) { + switch (path.Type) { + case 0: + TFile(path.Path, CreateAlways | RdWr); + break; + + case 1: + MakeDirIfNotExist(path.Path.data()); + break; + + case 2: + ythrow yexception() << "unknown path type"; + } + + Paths_.push_back(path); + Srch_[path.Path] = path; + } + + inline int Type(const TString& path) { + THashMap::const_iterator it = Srch_.find(path); + + UNIT_ASSERT(it != Srch_.end()); + + return it->second.Type; + } + + inline bool Have(const TString& path, int type) { + return Type(path) == type; + } + + inline ~TDirHier() { + for (size_t i = 0; i < Paths_.size(); ++i) { + NFs::Remove(Paths_[Paths_.size() - i - 1].Path); + } + } + + private: + TVector Paths_; + THashMap Srch_; + }; + + inline void TestLocal() { + TString dirname("." LOCSLASH_S); + TDirIterator d(dirname, FTS_NOCHDIR); + for (auto it = d.begin(); it != d.end(); ++it) { + } + } + + inline void TestIt() { + TDirHier hier; + + const TString dir = "tmpdir"; + const TDirHier::TPath path = {dir, 1}; + + hier.Add(path); + + for (size_t i = 0; i < 10; ++i) { + const TString dir1 = dir + LOCSLASH_C + ToString(i); + const TDirHier::TPath path1 = {dir1, 1}; + + hier.Add(path1); + + for (size_t j = 0; j < 10; ++j) { + const TString subdir2 = ToString(j); + const TString dir2 = dir1 + LOCSLASH_C + subdir2; + const TDirHier::TPath path2 = {dir2, 1}; + + hier.Add(path2); + + for (size_t k = 0; k < 3; ++k) { + const TString file = dir2 + LOCSLASH_C + "file" + ToString(k); + const TDirHier::TPath fpath = {file, 0}; + + hier.Add(fpath); + } + } + } + + TDirIterator d(dir); + + for (auto it = d.begin(); it != d.end(); ++it) { + UNIT_ASSERT(hier.Have(it->fts_path, it->fts_info != FTS_F)); + } + } + + inline void TestSkip() { + TDirHier hier; + + const TString dir = "tmpdir"; + const TDirHier::TPath path = {dir, 1}; + + hier.Add(path); + hier.AddDir(dir + LOCSLASH_C + "dir1"); + hier.AddDir(dir + LOCSLASH_C + "dir1" + LOCSLASH_C + "dir2"); + // + // Without skip + // + { + TDirIterator di(dir); + + UNIT_ASSERT(di.Next()); + UNIT_ASSERT_EQUAL(TStringBuf(di.Next()->fts_name), "dir1"); + UNIT_ASSERT_EQUAL(TStringBuf(di.Next()->fts_name), "dir2"); + UNIT_ASSERT_EQUAL(TStringBuf(di.Next()->fts_name), "dir2"); + UNIT_ASSERT_EQUAL(TStringBuf(di.Next()->fts_name), "dir1"); + UNIT_ASSERT(di.Next()); + UNIT_ASSERT_EQUAL(di.Next(), nullptr); + } + // + // With skip + // + { + TDirIterator di(dir); + + UNIT_ASSERT(di.Next()); + auto ent = di.Next(); + UNIT_ASSERT_EQUAL(TStringBuf(ent->fts_name), "dir1"); + di.Skip(ent); + UNIT_ASSERT_EQUAL(TStringBuf(di.Next()->fts_name), "dir1"); + UNIT_ASSERT(di.Next()); + UNIT_ASSERT_EQUAL(di.Next(), nullptr); + } + } + + inline void TestSort() { + TDirHier dh; + const TString dir("tmpdir"); + + //prepare fs + { + TMersenne rnd; + const TString prefixes[] = { + "a", "b", "xxx", "111", ""}; + + dh.AddDir(dir); + + for (size_t i = 0; i < 100; ++i) { + const TString fname = dir + LOCSLASH_C + prefixes[i % Y_ARRAY_SIZE(prefixes)] + ToString(rnd.GenRand()); + + dh.AddFile(fname); + } + } + + TVector fnames; + + { + TDirIterator d(dir, TDirIterator::TOptions().SetSortByName()); + + for (auto it = d.begin(); it != d.end(); ++it) { + if (it->fts_info == FTS_F) { + fnames.push_back(it->fts_name); + } + } + } + + TVector sorted(fnames); + Sort(sorted.begin(), sorted.end()); + + UNIT_ASSERT_VALUES_EQUAL(JoinWithNewline(fnames), JoinWithNewline(sorted)); + } + + inline void TestError() { + UNIT_ASSERT_EXCEPTION(TDirIterator("./notexistingfilename"), TDirIterator::TError); + } +}; + +UNIT_TEST_SUITE_REGISTRATION(TDirIteratorTest); diff --git a/util/folder/lstat_win.c b/util/folder/lstat_win.c new file mode 100644 index 00000000000..cf94cec01ae --- /dev/null +++ b/util/folder/lstat_win.c @@ -0,0 +1,35 @@ +#include + +#ifdef _win_ + #include + #include "lstat_win.h" + +int lstat(const char* fileName, stat_struct* fileStat) { + int len = strlen(fileName); + int convRes = MultiByteToWideChar(CP_UTF8, 0, fileName, len, 0, 0); + if (convRes == 0) { + return -1; + } + WCHAR* buf = malloc(sizeof(WCHAR) * (convRes + 1)); + MultiByteToWideChar(CP_UTF8, 0, fileName, len, buf, convRes); + buf[convRes] = 0; + + HANDLE findHandle; + WIN32_FIND_DATAW findBuf; + int result; + result = _wstat64(buf, fileStat); + if (result == 0) { + SetLastError(0); + findHandle = FindFirstFileW(buf, &findBuf); + if (findBuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && + (findBuf.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT || findBuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) + { + fileStat->st_mode = fileStat->st_mode & ~_S_IFMT | _S_IFLNK; + } + FindClose(findHandle); + } + free(buf); + return result; +} + +#endif //_win_ diff --git a/util/folder/lstat_win.h b/util/folder/lstat_win.h new file mode 100644 index 00000000000..0bf7c19055b --- /dev/null +++ b/util/folder/lstat_win.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "fts.h" + +#ifdef _win_ + #include + + #ifdef __cplusplus +extern "C" { + #endif + + #define _S_IFLNK 0xA000 + int lstat(const char* fileName, stat_struct* fileStat); + + #ifdef __cplusplus +} + #endif + +#endif //_win_ diff --git a/util/folder/path.cpp b/util/folder/path.cpp new file mode 100644 index 00000000000..940f6dffe0d --- /dev/null +++ b/util/folder/path.cpp @@ -0,0 +1,537 @@ +#include "dirut.h" +#include "path.h" +#include "pathsplit.h" + +#include +#include +#include +#include +#include +#include + +struct TFsPath::TSplit: public TAtomicRefCount, public TPathSplit { + inline TSplit(const TStringBuf path) + : TPathSplit(path) + { + } + inline TSplit(const TString& path, const TSimpleIntrusivePtr& thatSplit, const TString::char_type* thatPathData) { + for (const auto& thatPart : *thatSplit) { + emplace_back(path.data() + (thatPart.data() - thatPathData), thatPart.size()); + } + + if (!thatSplit->Drive.empty()) { + Drive = TStringBuf(path.data() + (thatSplit->Drive.data() - thatPathData), thatSplit->Drive.size()); + } + + IsAbsolute = thatSplit->IsAbsolute; + } +}; + +void TFsPath::CheckDefined() const { + if (!IsDefined()) { + ythrow TIoException() << TStringBuf("must be defined"); + } +} + +bool TFsPath::IsSubpathOf(const TFsPath& that) const { + const TSplit& split = GetSplit(); + const TSplit& rsplit = that.GetSplit(); + + if (rsplit.IsAbsolute != split.IsAbsolute) { + return false; + } + + if (rsplit.Drive != split.Drive) { + return false; + } + + if (rsplit.size() >= split.size()) { + return false; + } + + return std::equal(rsplit.begin(), rsplit.end(), split.begin()); +} + +bool TFsPath::IsNonStrictSubpathOf(const TFsPath& that) const { + const TSplit& split = GetSplit(); + const TSplit& rsplit = that.GetSplit(); + + if (rsplit.IsAbsolute != split.IsAbsolute) { + return false; + } + + if (rsplit.Drive != split.Drive) { + return false; + } + + if (rsplit.size() > split.size()) { + return false; + } + + return std::equal(rsplit.begin(), rsplit.end(), split.begin()); +} + +TFsPath TFsPath::RelativeTo(const TFsPath& root) const { + TSplit split = GetSplit(); + const TSplit& rsplit = root.GetSplit(); + + if (split.Reconstruct() == rsplit.Reconstruct()) { + return TFsPath(); + } + + if (!this->IsSubpathOf(root)) { + ythrow TIoException() << "path " << *this << " is not subpath of " << root; + } + + split.erase(split.begin(), split.begin() + rsplit.size()); + split.IsAbsolute = false; + + return TFsPath(split.Reconstruct()); +} + +TFsPath TFsPath::RelativePath(const TFsPath& root) const { + TSplit split = GetSplit(); + const TSplit& rsplit = root.GetSplit(); + size_t cnt = 0; + + while (split.size() > cnt && rsplit.size() > cnt && split[cnt] == rsplit[cnt]) { + ++cnt; + } + bool absboth = split.IsAbsolute && rsplit.IsAbsolute; + if (cnt == 0 && !absboth) { + ythrow TIoException() << "No common parts in " << *this << " and " << root; + } + TString r; + for (size_t i = 0; i < rsplit.size() - cnt; i++) { + r += i == 0 ? ".." : "/.."; + } + for (size_t i = cnt; i < split.size(); i++) { + r += (i == 0 || i == cnt && rsplit.size() - cnt == 0 ? "" : "/"); + r += split[i]; + } + return r.size() ? TFsPath(r) : TFsPath(); +} + +TFsPath TFsPath::Parent() const { + if (!IsDefined()) { + return TFsPath(); + } + + TSplit split = GetSplit(); + if (split.size()) { + split.pop_back(); + } + if (!split.size() && !split.IsAbsolute) { + return TFsPath("."); + } + return TFsPath(split.Reconstruct()); +} + +TFsPath& TFsPath::operator/=(const TFsPath& that) { + if (!IsDefined()) { + *this = that; + + } else if (that.IsDefined() && that.GetPath() != ".") { + if (!that.IsRelative()) { + ythrow TIoException() << "path should be relative: " << that.GetPath(); + } + + TSplit split = GetSplit(); + const TSplit& rsplit = that.GetSplit(); + split.insert(split.end(), rsplit.begin(), rsplit.end()); + *this = TFsPath(split.Reconstruct()); + } + return *this; +} + +TFsPath& TFsPath::Fix() { + // just normalize via reconstruction + TFsPath(GetSplit().Reconstruct()).Swap(*this); + + return *this; +} + +TString TFsPath::GetName() const { + if (!IsDefined()) { + return TString(); + } + + const TSplit& split = GetSplit(); + + if (split.size() > 0) { + if (split.back() != "..") { + return TString(split.back()); + } else { + // cannot just drop last component, because path itself may be a symlink + return RealPath().GetName(); + } + } else { + if (split.IsAbsolute) { + return split.Reconstruct(); + } else { + return Cwd().GetName(); + } + } +} + +TString TFsPath::GetExtension() const { + return TString(GetSplit().Extension()); +} + +bool TFsPath::IsAbsolute() const { + return GetSplit().IsAbsolute; +} + +bool TFsPath::IsRelative() const { + return !IsAbsolute(); +} + +void TFsPath::InitSplit() const { + Split_ = new TSplit(Path_); +} + +TFsPath::TSplit& TFsPath::GetSplit() const { + // XXX: race condition here + if (!Split_) { + InitSplit(); + } + return *Split_; +} + +static Y_FORCE_INLINE void VerifyPath(const TStringBuf path) { + Y_ABORT_UNLESS(!path.Contains('\0'), "wrong format of TFsPath: %s", EscapeC(path).c_str()); +} + +TFsPath::TFsPath() { +} + +TFsPath::TFsPath(const TString& path) + : Path_(path) +{ + VerifyPath(Path_); +} + +TFsPath::TFsPath(const TStringBuf path) + : Path_(ToString(path)) +{ + VerifyPath(Path_); +} + +TFsPath::TFsPath(const char* path) + : Path_(path) +{ +} + +TFsPath::TFsPath(const TFsPath& that) { + *this = that; +} + +TFsPath::TFsPath(TFsPath&& that) { + *this = std::move(that); +} + +TFsPath& TFsPath::operator=(const TFsPath& that) { + Path_ = that.Path_; + if (that.Split_) { + Split_ = new TSplit(Path_, that.Split_, that.Path_.begin()); + } else { + Split_ = nullptr; + } + return *this; +} + +TFsPath& TFsPath::operator=(TFsPath&& that) { +#ifdef TSTRING_IS_STD_STRING + const auto thatPathData = that.Path_.data(); + Path_ = std::move(that.Path_); + if (that.Split_) { + if (Path_.data() == thatPathData) { // Path_ moved, can move Split_ + Split_ = std::move(that.Split_); + } else { // Path_ copied, rebuild Split_ using that.Split_ + Split_ = new TSplit(Path_, that.Split_, that.Path_.data()); + } + } else { + Split_ = nullptr; + } +#else + Path_ = std::move(that.Path_); + Split_ = std::move(that.Split_); +#endif + return *this; +} + +TFsPath TFsPath::Child(const TString& name) const { + if (!name) { + ythrow TIoException() << "child name must not be empty"; + } + + return *this / name; +} + +struct TClosedir { + static void Destroy(DIR* dir) { + if (dir) { + if (0 != closedir(dir)) { + ythrow TIoSystemError() << "failed to closedir"; + } + } + } +}; + +void TFsPath::ListNames(TVector& children) const { + CheckDefined(); + THolder dir(opendir(this->c_str())); + if (!dir) { + ythrow TIoSystemError() << "failed to opendir " << Path_; + } + + for (;;) { + struct dirent de; + struct dirent* ok; + // TODO(yazevnul|IGNIETFERRO-1070): remove these macroses by replacing `readdir_r` with proper + // alternative + Y_PRAGMA_DIAGNOSTIC_PUSH + Y_PRAGMA_NO_DEPRECATED + int r = readdir_r(dir.Get(), &de, &ok); + Y_PRAGMA_DIAGNOSTIC_POP + if (r != 0) { + ythrow TIoSystemError() << "failed to readdir " << Path_; + } + if (ok == nullptr) { + return; + } + TString name(de.d_name); + if (name == "." || name == "..") { + continue; + } + children.push_back(name); + } +} + +bool TFsPath::Contains(const TString& component) const { + if (!IsDefined()) { + return false; + } + + TFsPath path = *this; + while (path.Parent() != path) { + if (path.GetName() == component) { + return true; + } + + path = path.Parent(); + } + + return false; +} + +void TFsPath::List(TVector& files) const { + TVector names; + ListNames(names); + for (auto& name : names) { + files.push_back(Child(name)); + } +} + +void TFsPath::RenameTo(const TString& newPath) const { + CheckDefined(); + if (!newPath) { + ythrow TIoException() << "bad new file name"; + } + if (!NFs::Rename(Path_, newPath)) { + ythrow TIoSystemError() << "failed to rename " << Path_ << " to " << newPath; + } +} + +void TFsPath::RenameTo(const char* newPath) const { + RenameTo(TString(newPath)); +} + +void TFsPath::RenameTo(const TFsPath& newPath) const { + RenameTo(newPath.GetPath()); +} + +void TFsPath::Touch() const { + CheckDefined(); + if (!TFile(*this, OpenAlways).IsOpen()) { + ythrow TIoException() << "failed to touch " << *this; + } +} + +// XXX: move implementation to util/somewhere. +TFsPath TFsPath::RealPath() const { + CheckDefined(); + return ::RealPath(*this); +} + +TFsPath TFsPath::RealLocation() const { + CheckDefined(); + return ::RealLocation(*this); +} + +TFsPath TFsPath::ReadLink() const { + CheckDefined(); + + if (!IsSymlink()) { + ythrow TIoException() << "not a symlink " << *this; + } + + return NFs::ReadLink(*this); +} + +bool TFsPath::Exists() const { + return IsDefined() && NFs::Exists(*this); +} + +void TFsPath::CheckExists() const { + if (!Exists()) { + ythrow TIoException() << "path does not exist " << Path_; + } +} + +bool TFsPath::IsDirectory() const { + return IsDefined() && TFileStat(GetPath().data()).IsDir(); +} + +bool TFsPath::IsFile() const { + return IsDefined() && TFileStat(GetPath().data()).IsFile(); +} + +bool TFsPath::IsSymlink() const { + return IsDefined() && TFileStat(GetPath().data(), true).IsSymlink(); +} + +void TFsPath::DeleteIfExists() const { + if (!IsDefined()) { + return; + } + + ::unlink(this->c_str()); + ::rmdir(this->c_str()); + if (Exists()) { + ythrow TIoException() << "failed to delete " << Path_; + } +} + +void TFsPath::MkDir(const int mode) const { + CheckDefined(); + if (!Exists()) { + int r = Mkdir(this->c_str(), mode); + if (r != 0) { + // TODO (stanly) will still fail on Windows because + // LastSystemError() returns windows specific ERROR_ALREADY_EXISTS + // instead of EEXIST. + if (LastSystemError() != EEXIST) { + ythrow TIoSystemError() << "could not create directory " << Path_; + } + } + } +} + +void TFsPath::MkDirs(const int mode) const { + CheckDefined(); + if (!Exists()) { + Parent().MkDirs(mode); + MkDir(mode); + } +} + +void TFsPath::ForceDelete() const { + if (!IsDefined()) { + return; + } + + TFileStat stat(GetPath().c_str(), true); + if (stat.IsNull()) { + const int err = LastSystemError(); +#ifdef _win_ + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { +#else + if (err == ENOENT) { +#endif + return; + } else { + ythrow TIoException() << "failed to stat " << Path_; + } + } + + bool succ; + if (stat.IsDir()) { + TVector children; + List(children); + for (auto& i : children) { + i.ForceDelete(); + } + succ = ::rmdir(this->c_str()) == 0; + } else { + succ = ::unlink(this->c_str()) == 0; + } + + if (!succ && LastSystemError()) { + ythrow TIoException() << "failed to delete " << Path_; + } +} + +void TFsPath::CopyTo(const TString& newPath, bool force) const { + if (IsDirectory()) { + if (force) { + TFsPath(newPath).MkDirs(); + } else if (!TFsPath(newPath).IsDirectory()) { + ythrow TIoException() << "Target path is not a directory " << newPath; + } + TVector children; + List(children); + for (auto&& i : children) { + i.CopyTo(newPath + "/" + i.GetName(), force); + } + } else { + if (force) { + TFsPath(newPath).Parent().MkDirs(); + } else { + if (!TFsPath(newPath).Parent().IsDirectory()) { + ythrow TIoException() << "Parent (" << TFsPath(newPath).Parent() << ") of a target path is not a directory " << newPath; + } + if (TFsPath(newPath).Exists()) { + ythrow TIoException() << "Path already exists " << newPath; + } + } + NFs::Copy(Path_, newPath); + } +} + +void TFsPath::ForceRenameTo(const TString& newPath) const { + try { + RenameTo(newPath); + } catch (const TIoSystemError& /* error */) { + CopyTo(newPath, true); + ForceDelete(); + } +} + +TFsPath TFsPath::Cwd() { + return TFsPath(::NFs::CurrentWorkingDirectory()); +} + +const TPathSplit& TFsPath::PathSplit() const { + return GetSplit(); +} + +template <> +void Out(IOutputStream& os, const TFsPath& f) { + os << f.GetPath(); +} + +template <> +TFsPath FromStringImpl(const char* s, size_t len) { + return TFsPath{TStringBuf{s, len}}; +} + +template <> +bool TryFromStringImpl(const char* s, size_t len, TFsPath& result) { + try { + result = TStringBuf{s, len}; + return true; + } catch (std::exception&) { + return false; + } +} diff --git a/util/folder/path.h b/util/folder/path.h new file mode 100644 index 00000000000..9f6642d5cb8 --- /dev/null +++ b/util/folder/path.h @@ -0,0 +1,235 @@ +#pragma once + +#include "fwd.h" +#include "pathsplit.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * Class behaviour is platform-dependent. + * It uses platform-dependent separators for path-reconstructing operations. + */ +class TFsPath { +private: + struct TSplit; + +public: + TFsPath(); + TFsPath(const TString& path); + TFsPath(const TStringBuf path); + TFsPath(const char* path); + + TFsPath(const std::string& path) + : TFsPath(TStringBuf(path)) + { + } + + TFsPath(const TFsPath& that); + TFsPath(TFsPath&& that); + + TFsPath& operator=(const TFsPath& that); + TFsPath& operator=(TFsPath&& that); + + ~TFsPath() = default; + + void CheckDefined() const; + + inline bool IsDefined() const { + return Path_.length() > 0; + } + + inline explicit operator bool() const { + return IsDefined(); + } + + inline const char* c_str() const { + return Path_.c_str(); + } + + inline operator const TString&() const { + return Path_; + } + + inline bool operator==(const TFsPath& that) const { + return Path_ == that.Path_; + } + + TFsPath& operator/=(const TFsPath& that); + + friend TFsPath operator/(const TFsPath& s, const TFsPath& p) { + TFsPath ret(s); + return ret /= p; + } + + const TPathSplit& PathSplit() const; + + TFsPath& Fix(); + + inline const TString& GetPath() const { + return Path_; + } + + /// last component of path, or "/" if root + TString GetName() const; + + /** + * "a.b.tmp" -> "tmp" + * "a.tmp" -> "tmp" + * ".tmp" -> "" + */ + TString GetExtension() const; + + bool IsAbsolute() const; + bool IsRelative() const; + + /** + * TFsPath("/a/b").IsSubpathOf("/a") -> true + * + * TFsPath("/a").IsSubpathOf("/a") -> false + * + * TFsPath("/a").IsSubpathOf("/other/path") -> false + * @param that - presumable parent path of this + * @return True if this is a subpath of that and false otherwise. + */ + bool IsSubpathOf(const TFsPath& that) const; + + /** + * TFsPath("/a/b").IsNonStrictSubpathOf("/a") -> true + * + * TFsPath("/a").IsNonStrictSubpathOf("/a") -> true + * + * TFsPath("/a").IsNonStrictSubpathOf("/other/path") -> false + * @param that - presumable parent path of this + * @return True if this is a subpath of that or they are equivalent and false otherwise. + */ + bool IsNonStrictSubpathOf(const TFsPath& that) const; + + bool IsContainerOf(const TFsPath& that) const { + return that.IsSubpathOf(*this); + } + + TFsPath RelativeTo(const TFsPath& root) const; // must be subpath of root + + /** + * @returns relative path or empty path if root equals to this. + */ + TFsPath RelativePath(const TFsPath& root) const; //..; for relative paths 1st component must be the same + + /** + * Never fails. Returns this if already a root. + */ + TFsPath Parent() const; + + TString Basename() const { + return GetName(); + } + TString Dirname() const { + return Parent(); + } + + TFsPath Child(const TString& name) const; + + /** + * @brief create this directory + * + * @param mode specifies permissions to use as described in mkdir(2), makes sense only on Unix-like systems. + * + * Nothing to do if dir exists. + */ + void MkDir(const int mode = MODE0777) const; + + /** + * @brief create this directory and all parent directories as needed + * + * @param mode specifies permissions to use as described in mkdir(2), makes sense only on Unix-like systems. + */ + void MkDirs(const int mode = MODE0777) const; + + // XXX: rewrite to return iterator + void List(TVector& children) const; + void ListNames(TVector& children) const; + + // Check, if path contains at least one component with a specific name. + bool Contains(const TString& component) const; + + // fails to delete non-empty directory + void DeleteIfExists() const; + // delete recursively. Does nothing if not exists + void ForceDelete() const; + + // XXX: ino + + inline bool Stat(TFileStat& stat) const { + stat = TFileStat(Path_.data()); + + return stat.Mode; + } + + bool Exists() const; + /// false if not exists + bool IsDirectory() const; + /// false if not exists + bool IsFile() const; + /// false if not exists + bool IsSymlink() const; + /// throw TIoException if not exists + void CheckExists() const; + + void RenameTo(const TString& newPath) const; + void RenameTo(const char* newPath) const; + void RenameTo(const TFsPath& newFile) const; + void ForceRenameTo(const TString& newPath) const; + + void CopyTo(const TString& newPath, bool force) const; + + void Touch() const; + + TFsPath RealPath() const; + TFsPath RealLocation() const; + TFsPath ReadLink() const; + + /// always absolute + static TFsPath Cwd(); + + inline void Swap(TFsPath& p) noexcept { + DoSwap(Path_, p.Path_); + Split_.Swap(p.Split_); + } + +private: + void InitSplit() const; + TSplit& GetSplit() const; + +private: + TString Path_; + /// cache + mutable TSimpleIntrusivePtr Split_; +}; + +namespace NPrivate { + inline void AppendToFsPath(TFsPath&) { + } + + template + void AppendToFsPath(TFsPath& fsPath, const T& arg, Ts&&... args) { + fsPath /= TFsPath(arg); + AppendToFsPath(fsPath, std::forward(args)...); + } +} + +template +TString JoinFsPaths(Ts&&... args) { + TFsPath fsPath; + ::NPrivate::AppendToFsPath(fsPath, std::forward(args)...); + return fsPath.GetPath(); +} diff --git a/util/folder/path.pxd b/util/folder/path.pxd new file mode 100644 index 00000000000..85af10d746b --- /dev/null +++ b/util/folder/path.pxd @@ -0,0 +1,93 @@ +from util.generic.string cimport TString, TStringBuf +from util.generic.vector cimport TVector + + +# NOTE (danila-eremin) Currently not possible to use `const` and `except +` at the same time, so some function not marked const +cdef extern from "util/folder/path.h" nogil: + cdef cppclass TFsPath: + TFsPath() except + + TFsPath(const TString&) except + + TFsPath(const TStringBuf) except + + TFsPath(const char*) except + + + void CheckDefined() except + + + bint IsDefined() const + bint operator bool() const + + const char* c_str() const + + bint operator==(const TFsPath&) const + bint operator!=(const TFsPath&) const + + # NOTE (danila-eremin) operator `/=` Not supported + # TFsPath& operator/=(const TFsPath&) const + + TFsPath operator/(const TFsPath&, const TFsPath&) except + + + # NOTE (danila-eremin) TPathSplit needed + # const TPathSplit& PathSplit() const + + TFsPath& Fix() except + + + const TString& GetPath() const + TString GetName() const + + TString GetExtension() const + + bint IsAbsolute() const + bint IsRelative() const + + bint IsSubpathOf(const TFsPath&) const + bint IsNonStrictSubpathOf(const TFsPath&) const + bint IsContainerOf(const TFsPath&) const + + TFsPath RelativeTo(const TFsPath&) except + + TFsPath RelativePath(const TFsPath&) except + + + TFsPath Parent() const + + TString Basename() const + TString Dirname() const + + TFsPath Child(const TString&) except + + + void MkDir() except + + void MkDir(const int) except + + void MkDirs() except + + void MkDirs(const int) except + + + void List(TVector[TFsPath]&) except + + void ListNames(TVector[TString]&) except + + + bint Contains(const TString&) const + + void DeleteIfExists() except + + void ForceDelete() except + + + # NOTE (danila-eremin) TFileStat needed + # bint Stat(TFileStat&) const + + bint Exists() const + bint IsDirectory() const + bint IsFile() const + bint IsSymlink() const + void CheckExists() except + + + void RenameTo(const TString&) except + + void RenameTo(const char*) except + + void RenameTo(const TFsPath&) except + + void ForceRenameTo(const TString&) except + + + void CopyTo(const TString&, bint) except + + + void Touch() except + + + TFsPath RealPath() except + + TFsPath RealLocation() except + + TFsPath ReadLink() except + + + @staticmethod + TFsPath Cwd() except + + + void Swap(TFsPath&) diff --git a/util/folder/path_ut.cpp b/util/folder/path_ut.cpp new file mode 100644 index 00000000000..bb0656ddbc5 --- /dev/null +++ b/util/folder/path_ut.cpp @@ -0,0 +1,897 @@ +#include "path.h" +#include "pathsplit.h" +#include "dirut.h" +#include "tempdir.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef _win_ + #include +#endif + +namespace { + /// empty directory for test that needs filesystem + /// recreates directory in constructor and removes directory in destructor + class TTestDirectory { + private: + TFsPath Path_; + + public: + TTestDirectory(const TString& name); + ~TTestDirectory(); + + TFsPath GetFsPath() const { + return Path_; + } + + TFsPath Child(const TString& name) const { + return Path_.Child(name); + } + }; + + TTestDirectory::TTestDirectory(const TString& name) { + Y_ABORT_UNLESS(name.length() > 0, "have to specify name"); + Y_ABORT_UNLESS(name.find('.') == TString::npos, "must be simple name"); + Y_ABORT_UNLESS(name.find('/') == TString::npos, "must be simple name"); + Y_ABORT_UNLESS(name.find('\\') == TString::npos, "must be simple name"); + Path_ = TFsPath(name); + + Path_.ForceDelete(); + Path_.MkDir(); + } + + TTestDirectory::~TTestDirectory() { + Path_.ForceDelete(); + } +} + +Y_UNIT_TEST_SUITE(TFsPathTests) { + Y_UNIT_TEST(TestMkDirs) { + const TFsPath path = "a/b/c/d/e/f"; + path.ForceDelete(); + TFsPath current = path; + ui32 checksCounter = 0; + while (current != ".") { + UNIT_ASSERT(!path.Exists()); + ++checksCounter; + current = current.Parent(); + } + UNIT_ASSERT_VALUES_EQUAL(checksCounter, 6); + + path.MkDirs(); + UNIT_ASSERT(path.Exists()); + + current = path; + while (current != ".") { + UNIT_ASSERT(path.Exists()); + current = current.Parent(); + } + } + + Y_UNIT_TEST(MkDirFreak) { + TFsPath path; + UNIT_ASSERT_EXCEPTION(path.MkDir(), TIoException); + UNIT_ASSERT_EXCEPTION(path.MkDirs(), TIoException); + path = "."; + path.MkDir(); + path.MkDirs(); + } + + Y_UNIT_TEST(Parent) { +#ifdef _win_ + UNIT_ASSERT_VALUES_EQUAL(TFsPath("\\etc/passwd").Parent(), TFsPath("\\etc")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("\\etc").Parent(), TFsPath("\\")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("\\").Parent(), TFsPath("\\")); + + UNIT_ASSERT_VALUES_EQUAL(TFsPath("etc\\passwd").Parent(), TFsPath("etc")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("etc").Parent(), TFsPath(".")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath(".\\etc").Parent(), TFsPath(".")); + + UNIT_ASSERT_VALUES_EQUAL(TFsPath("C:\\etc/passwd").Parent(), TFsPath("C:\\etc")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("C:\\etc").Parent(), TFsPath("C:\\")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("C:\\").Parent(), TFsPath("C:\\")); +#else + UNIT_ASSERT_VALUES_EQUAL(TFsPath("/etc/passwd").Parent(), TFsPath("/etc")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("/etc").Parent(), TFsPath("/")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("/").Parent(), TFsPath("/")); + + UNIT_ASSERT_VALUES_EQUAL(TFsPath("etc/passwd").Parent(), TFsPath("etc")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("etc").Parent(), TFsPath(".")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("./etc").Parent(), TFsPath(".")); +#endif + +#if 0 + UNIT_ASSERT_VALUES_EQUAL(TFsPath("./etc/passwd").Parent(), TFsPath("./etc")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("./").Parent(), TFsPath("..")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath(".").Parent(), TFsPath("..")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("..").Parent(), TFsPath("../..")); +#endif + } + + Y_UNIT_TEST(GetName) { + TTestDirectory d("GetName"); + UNIT_ASSERT_VALUES_EQUAL(TString("dfgh"), d.Child("dfgh").GetName()); + + // check does not fail + TFsPath(".").GetName(); + +#ifdef _unix_ + UNIT_ASSERT_VALUES_EQUAL(TString("/"), TFsPath("/").GetName()); +#endif + } + + Y_UNIT_TEST(GetExtension) { + TTestDirectory d("GetExtension"); + UNIT_ASSERT_VALUES_EQUAL("", d.Child("a").GetExtension()); + UNIT_ASSERT_VALUES_EQUAL("", d.Child(".a").GetExtension()); + UNIT_ASSERT_VALUES_EQUAL("", d.Child("zlib").GetExtension()); + UNIT_ASSERT_VALUES_EQUAL("zlib", d.Child("file.zlib").GetExtension()); + UNIT_ASSERT_VALUES_EQUAL("zlib", d.Child("file.ylib.zlib").GetExtension()); + } + + Y_UNIT_TEST(TestRename) { + TTestDirectory xx("TestRename"); + TFsPath f1 = xx.Child("f1"); + TFsPath f2 = xx.Child("f2"); + f1.Touch(); + f1.RenameTo(f2); + UNIT_ASSERT(!f1.Exists()); + UNIT_ASSERT(f2.Exists()); + } + + Y_UNIT_TEST(TestForceRename) { + TTestDirectory xx("TestForceRename"); + TFsPath fMain = xx.Child("main"); + + TFsPath f1 = fMain.Child("f1"); + f1.MkDirs(); + TFsPath f1Child = f1.Child("f1child"); + f1Child.Touch(); + + TFsPath f2 = fMain.Child("f2"); + f2.MkDirs(); + + fMain.ForceRenameTo("TestForceRename/main1"); + + UNIT_ASSERT(!xx.Child("main").Exists()); + UNIT_ASSERT(xx.Child("main1").Child("f1").Exists()); + UNIT_ASSERT(xx.Child("main1").Child("f2").Exists()); + UNIT_ASSERT(xx.Child("main1").Child("f1").Child("f1child").Exists()); + } + + Y_UNIT_TEST(TestRenameFail) { + UNIT_ASSERT_EXCEPTION(TFsPath("sfsfsfsdfsfsdfdf").RenameTo("sdfsdf"), TIoException); + } + +#ifndef _win_ + Y_UNIT_TEST(TestRealPath) { + UNIT_ASSERT(TFsPath(".").RealPath().IsDirectory()); + + TTestDirectory td("TestRealPath"); + TFsPath link = td.Child("link"); + TFsPath target1 = td.Child("target1"); + target1.Touch(); + TFsPath target2 = td.Child("target2"); + target2.Touch(); + UNIT_ASSERT(NFs::SymLink(target1.RealPath(), link.GetPath())); + UNIT_ASSERT_VALUES_EQUAL(link.RealPath(), target1.RealPath()); + UNIT_ASSERT(NFs::Remove(link.GetPath())); + UNIT_ASSERT(NFs::SymLink(target2.RealPath(), link.GetPath())); + UNIT_ASSERT_VALUES_EQUAL(link.RealPath(), target2.RealPath()); // must not cache old value + } +#endif + + Y_UNIT_TEST(TestSlashesAndBasename) { + TFsPath p("/db/BASE/primus121-025-1380131338//"); + UNIT_ASSERT_VALUES_EQUAL(p.Basename(), TString("primus121-025-1380131338")); + TFsPath testP = p / "test"; +#ifdef _win_ + UNIT_ASSERT_VALUES_EQUAL(testP.GetPath(), "\\db\\BASE\\primus121-025-1380131338\\test"); +#else + UNIT_ASSERT_VALUES_EQUAL(testP.GetPath(), "/db/BASE/primus121-025-1380131338/test"); +#endif + } + + Y_UNIT_TEST(TestSlashesAndBasenameWin) { + TFsPath p("\\db\\BASE\\primus121-025-1380131338\\\\"); + TFsPath testP = p / "test"; +#ifdef _win_ + UNIT_ASSERT_VALUES_EQUAL(p.Basename(), TString("primus121-025-1380131338")); + UNIT_ASSERT_VALUES_EQUAL(testP.GetPath(), "\\db\\BASE\\primus121-025-1380131338\\test"); +#else + UNIT_ASSERT_VALUES_EQUAL(p.Basename(), TString("\\db\\BASE\\primus121-025-1380131338\\\\")); + UNIT_ASSERT_VALUES_EQUAL(testP.GetPath(), "\\db\\BASE\\primus121-025-1380131338\\\\/test"); +#endif + } + + Y_UNIT_TEST(TestSlashesAndBasenameWinDrive) { + TFsPath p("C:\\db\\BASE\\primus121-025-1380131338\\\\"); + TFsPath testP = p / "test"; +#ifdef _win_ + UNIT_ASSERT_VALUES_EQUAL(p.Basename(), TString("primus121-025-1380131338")); + UNIT_ASSERT_VALUES_EQUAL(testP.GetPath(), "C:\\db\\BASE\\primus121-025-1380131338\\test"); +#else + UNIT_ASSERT_VALUES_EQUAL(p.Basename(), TString("C:\\db\\BASE\\primus121-025-1380131338\\\\")); + UNIT_ASSERT_VALUES_EQUAL(testP.GetPath(), "C:\\db\\BASE\\primus121-025-1380131338\\\\/test"); +#endif + } + + Y_UNIT_TEST(TestList) { + TTestDirectory td("TestList-dir"); + + TFsPath dir = td.GetFsPath(); + dir.Child("a").Touch(); + dir.Child("b").MkDir(); + dir.Child("b").Child("b-1").Touch(); + dir.Child("c").MkDir(); + dir.Child("d").Touch(); + + TVector children; + dir.ListNames(children); + std::sort(children.begin(), children.end()); + + TVector expected; + expected.push_back("a"); + expected.push_back("b"); + expected.push_back("c"); + expected.push_back("d"); + + UNIT_ASSERT_VALUES_EQUAL(expected, children); + } + +#ifdef _unix_ + Y_UNIT_TEST(MkDirMode) { + TTestDirectory td("MkDirMode"); + TFsPath subDir = td.Child("subdir"); + const int mode = MODE0775; + subDir.MkDir(mode); + TFileStat stat; + UNIT_ASSERT(subDir.Stat(stat)); + // mkdir(2) places umask(2) on mode argument. + const int mask = Umask(0); + Umask(mask); + UNIT_ASSERT_VALUES_EQUAL(stat.Mode& MODE0777, mode & ~mask); + } +#endif + + Y_UNIT_TEST(Cwd) { + UNIT_ASSERT_VALUES_EQUAL(TFsPath::Cwd().RealPath(), TFsPath(".").RealPath()); + } + + Y_UNIT_TEST(TestSubpathOf) { + UNIT_ASSERT(TFsPath("/a/b/c/d").IsSubpathOf("/a/b")); + + UNIT_ASSERT(TFsPath("/a").IsSubpathOf("/")); + UNIT_ASSERT(!TFsPath("/").IsSubpathOf("/a")); + UNIT_ASSERT(!TFsPath("/a").IsSubpathOf("/a")); + + UNIT_ASSERT(TFsPath("/a/b").IsSubpathOf("/a")); + UNIT_ASSERT(TFsPath("a/b").IsSubpathOf("a")); + UNIT_ASSERT(!TFsPath("/a/b").IsSubpathOf("/b")); + UNIT_ASSERT(!TFsPath("a/b").IsSubpathOf("b")); + + // mixing absolute/relative + UNIT_ASSERT(!TFsPath("a").IsSubpathOf("/")); + UNIT_ASSERT(!TFsPath("a").IsSubpathOf("/a")); + UNIT_ASSERT(!TFsPath("/a").IsSubpathOf("a")); + UNIT_ASSERT(!TFsPath("a/b").IsSubpathOf("/a")); + UNIT_ASSERT(!TFsPath("/a/b").IsSubpathOf("a")); + +#ifdef _win_ + UNIT_ASSERT(TFsPath("x:/a/b").IsSubpathOf("x:/a")); + UNIT_ASSERT(!TFsPath("x:/a/b").IsSubpathOf("y:/a")); + UNIT_ASSERT(!TFsPath("x:/a/b").IsSubpathOf("a")); +#endif + } + + Y_UNIT_TEST(TestNonStrictSubpathOf) { + UNIT_ASSERT(TFsPath("/a/b/c/d").IsNonStrictSubpathOf("/a/b")); + + UNIT_ASSERT(TFsPath("/a").IsNonStrictSubpathOf("/")); + UNIT_ASSERT(!TFsPath("/").IsNonStrictSubpathOf("/a")); + + UNIT_ASSERT(TFsPath("/a/b").IsNonStrictSubpathOf("/a")); + UNIT_ASSERT(TFsPath("a/b").IsNonStrictSubpathOf("a")); + UNIT_ASSERT(!TFsPath("/a/b").IsNonStrictSubpathOf("/b")); + UNIT_ASSERT(!TFsPath("a/b").IsNonStrictSubpathOf("b")); + + // mixing absolute/relative + UNIT_ASSERT(!TFsPath("a").IsNonStrictSubpathOf("/")); + UNIT_ASSERT(!TFsPath("a").IsNonStrictSubpathOf("/a")); + UNIT_ASSERT(!TFsPath("/a").IsNonStrictSubpathOf("a")); + UNIT_ASSERT(!TFsPath("a/b").IsNonStrictSubpathOf("/a")); + UNIT_ASSERT(!TFsPath("/a/b").IsNonStrictSubpathOf("a")); + + // equal paths + UNIT_ASSERT(TFsPath("").IsNonStrictSubpathOf("")); + UNIT_ASSERT(TFsPath("/").IsNonStrictSubpathOf("/")); + UNIT_ASSERT(TFsPath("a").IsNonStrictSubpathOf("a")); + UNIT_ASSERT(TFsPath("/a").IsNonStrictSubpathOf("/a")); + UNIT_ASSERT(TFsPath("/a").IsNonStrictSubpathOf("/a/")); + UNIT_ASSERT(TFsPath("/a/").IsNonStrictSubpathOf("/a")); + UNIT_ASSERT(TFsPath("/a/").IsNonStrictSubpathOf("/a/")); + +#ifdef _win_ + UNIT_ASSERT(TFsPath("x:/a/b").IsNonStrictSubpathOf("x:/a")); + + UNIT_ASSERT(TFsPath("x:/a").IsNonStrictSubpathOf("x:/a")); + UNIT_ASSERT(TFsPath("x:/a/").IsNonStrictSubpathOf("x:/a")); + UNIT_ASSERT(TFsPath("x:/a").IsNonStrictSubpathOf("x:/a/")); + UNIT_ASSERT(TFsPath("x:/a/").IsNonStrictSubpathOf("x:/a/")); + + UNIT_ASSERT(!TFsPath("x:/").IsNonStrictSubpathOf("y:/")); + UNIT_ASSERT(!TFsPath("x:/a/b").IsNonStrictSubpathOf("y:/a")); + UNIT_ASSERT(!TFsPath("x:/a/b").IsNonStrictSubpathOf("a")); +#endif + } + + Y_UNIT_TEST(TestRelativePath) { + UNIT_ASSERT_VALUES_EQUAL(TFsPath("/a/b/c/d").RelativePath(TFsPath("/a/b")), TFsPath("c/d")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("/a/b/c/d").RelativePath(TFsPath("/a/b/e/f")), TFsPath("../../c/d")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("/").RelativePath(TFsPath("/")), TFsPath()); + UNIT_ASSERT_VALUES_EQUAL(TFsPath(".").RelativePath(TFsPath(".")), TFsPath()); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("/a/c").RelativePath(TFsPath("/a/b/../c")), TFsPath()); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("a/.././b").RelativePath(TFsPath("b/c")), TFsPath("..")); + + UNIT_ASSERT_EXCEPTION(TFsPath("a/b/c").RelativePath(TFsPath("d/e")), TIoException); + } + + Y_UNIT_TEST(TestUndefined) { + UNIT_ASSERT_VALUES_EQUAL(TFsPath(), TFsPath("")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath(), TFsPath().Fix()); + + UNIT_ASSERT_VALUES_EQUAL(TFsPath() / TFsPath(), TFsPath()); +#ifdef _win_ + UNIT_ASSERT_VALUES_EQUAL(TFsPath("a\\b"), TFsPath() / TString("a\\b")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("a\\b"), "a\\b" / TFsPath()); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("\\a\\b"), TFsPath() / "\\a\\b"); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("\\a\\b"), "\\a\\b" / TFsPath()); +#else + UNIT_ASSERT_VALUES_EQUAL(TFsPath("a/b"), TFsPath() / TString("a/b")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("a/b"), "a/b" / TFsPath()); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("/a/b"), TFsPath() / "/a/b"); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("/a/b"), "/a/b" / TFsPath()); +#endif + UNIT_ASSERT_VALUES_EQUAL(TFsPath("."), TFsPath() / "."); + UNIT_ASSERT_VALUES_EQUAL(TFsPath("."), "." / TFsPath()); + + UNIT_ASSERT(TFsPath().PathSplit().empty()); + UNIT_ASSERT(!TFsPath().PathSplit().IsAbsolute); + UNIT_ASSERT(TFsPath().IsRelative()); // undefined path is relative + + UNIT_ASSERT_VALUES_EQUAL(TFsPath().GetPath(), ""); + UNIT_ASSERT_VALUES_EQUAL(TFsPath().GetName(), ""); + UNIT_ASSERT_VALUES_EQUAL(TFsPath().GetExtension(), ""); + + UNIT_ASSERT_VALUES_EQUAL(TFsPath().Parent(), TFsPath()); + UNIT_ASSERT_VALUES_EQUAL(TFsPath().Child("a"), TFsPath("a")); + UNIT_ASSERT_VALUES_EQUAL(TFsPath().Basename(), ""); + UNIT_ASSERT_VALUES_EQUAL(TFsPath().Dirname(), ""); + + UNIT_ASSERT(!TFsPath().IsSubpathOf("a/b")); + UNIT_ASSERT(TFsPath().IsContainerOf("a/b")); + UNIT_ASSERT(!TFsPath().IsContainerOf("/a/b")); +#ifdef _win_ + UNIT_ASSERT_VALUES_EQUAL(TFsPath("a\\b").RelativeTo(TFsPath()), TFsPath("a\\b")); +#else + UNIT_ASSERT_VALUES_EQUAL(TFsPath("a/b").RelativeTo(TFsPath()), TFsPath("a/b")); +#endif + + UNIT_ASSERT(!TFsPath().Exists()); + UNIT_ASSERT(!TFsPath().IsFile()); + UNIT_ASSERT(!TFsPath().IsDirectory()); + TFileStat stat; + UNIT_ASSERT(!TFsPath().Stat(stat)); + } + + Y_UNIT_TEST(TestJoinFsPaths) { +#ifdef _win_ + UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a\\b", "c\\d"), "a\\b\\c\\d"); + UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a\\b", "..\\c"), "a\\b\\..\\c"); + UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a\\b\\..\\c", "d"), "a\\c\\d"); + UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a", "b", "c", "d"), "a\\b\\c\\d"); + UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a\\b\\..\\c"), "a\\b\\..\\c"); + UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a\\b", ""), "a\\b"); +#else + UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a/b", "c/d"), "a/b/c/d"); + UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a/b", "../c"), "a/b/../c"); + UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a/b/../c", "d"), "a/c/d"); + UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a", "b", "c", "d"), "a/b/c/d"); + UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a/b/../c"), "a/b/../c"); + UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a/b", ""), "a/b"); +#endif + } + + Y_UNIT_TEST(TestStringCast) { + TFsPath pathOne; + UNIT_ASSERT(TryFromString("/a/b", pathOne)); + UNIT_ASSERT_VALUES_EQUAL(pathOne, TFsPath{"/a/b"}); + + TFsPath pathTwo; + UNIT_ASSERT_NO_EXCEPTION(TryFromString("/a/b", pathTwo)); + + UNIT_ASSERT_VALUES_EQUAL(FromString("/a/b"), TFsPath{"/a/b"}); + + TFsPath pathThree{"/a/b"}; + UNIT_ASSERT_VALUES_EQUAL(ToString(pathThree), "/a/b"); + } + +#ifdef _unix_ + Y_UNIT_TEST(TestRemoveSymlinkToDir) { + TTempDir tempDir; + TFsPath tempDirPath(tempDir()); + + const TString originDir = tempDirPath.Child("origin"); + MakePathIfNotExist(originDir.c_str()); + + const TString originFile = TFsPath(originDir).Child("data"); + { + TFixedBufferFileOutput out(originFile); + out << "data111!!!"; + } + + const TString link = tempDirPath.Child("origin_symlink"); + NFs::SymLink(originDir, link); + + TFsPath(link).ForceDelete(); + + UNIT_ASSERT(!NFs::Exists(link)); + UNIT_ASSERT(NFs::Exists(originFile)); + UNIT_ASSERT(NFs::Exists(originDir)); + } + + Y_UNIT_TEST(TestRemoveSymlinkToFile) { + TTempDir tempDir; + TFsPath tempDirPath(tempDir()); + + const TString originDir = tempDirPath.Child("origin"); + MakePathIfNotExist(originDir.c_str()); + + const TString originFile = TFsPath(originDir).Child("data"); + { + TFixedBufferFileOutput out(originFile); + out << "data111!!!"; + } + + const TString link = tempDirPath.Child("origin_symlink"); + NFs::SymLink(originFile, link); + + TFsPath(link).ForceDelete(); + + UNIT_ASSERT(!NFs::Exists(link)); + UNIT_ASSERT(NFs::Exists(originFile)); + UNIT_ASSERT(NFs::Exists(originDir)); + } + + Y_UNIT_TEST(TestRemoveDirWithSymlinkToDir) { + TTempDir tempDir; + TFsPath tempDirPath(tempDir()); + + const TString symlinkedDir = tempDirPath.Child("to_remove"); + MakePathIfNotExist(symlinkedDir.c_str()); + + const TString originDir = tempDirPath.Child("origin"); + MakePathIfNotExist(originDir.c_str()); + + const TString originFile = TFsPath(originDir).Child("data"); + { + TFixedBufferFileOutput out(originFile); + out << "data111!!!"; + } + + const TString symlinkedFile = TFsPath(symlinkedDir).Child("origin_symlink"); + NFs::SymLink(originDir, symlinkedFile); + + TFsPath(symlinkedDir).ForceDelete(); + + UNIT_ASSERT(!NFs::Exists(symlinkedFile)); + UNIT_ASSERT(!NFs::Exists(symlinkedDir)); + UNIT_ASSERT(NFs::Exists(originFile)); + UNIT_ASSERT(NFs::Exists(originDir)); + } + + Y_UNIT_TEST(TestRemoveDirWithSymlinkToFile) { + TTempDir tempDir; + TFsPath tempDirPath(tempDir()); + + const TString symlinkedDir = tempDirPath.Child("to_remove"); + MakePathIfNotExist(symlinkedDir.c_str()); + + const TString originDir = tempDirPath.Child("origin"); + MakePathIfNotExist(originDir.c_str()); + + const TString originFile = TFsPath(originDir).Child("data"); + { + TFixedBufferFileOutput out(originFile); + out << "data111!!!"; + } + + const TString symlinkedFile = TFsPath(symlinkedDir).Child("origin_symlink"); + NFs::SymLink(originFile, symlinkedFile); + + TFsPath(symlinkedDir).ForceDelete(); + + UNIT_ASSERT(!NFs::Exists(symlinkedFile)); + UNIT_ASSERT(!NFs::Exists(symlinkedDir)); + UNIT_ASSERT(NFs::Exists(originFile)); + UNIT_ASSERT(NFs::Exists(originDir)); + } +#endif + + Y_UNIT_TEST(TestForceDeleteNonexisting) { + TTempDir tempDir; + TFsPath nonexisting = TFsPath(tempDir()).Child("nonexisting"); + nonexisting.ForceDelete(); + } + + // Here we want to test that all possible errors during TFsPath::ForceDelete + // are properly handled. To do so we have to trigger fs operation errors in + // three points: + // 1. stat/GetFileInformationByHandle + // 2. opendir + // 3. unlink/rmdir + // + // On unix systems we can achieve this by simply setting access rights on + // entry being deleted and its parent. But on windows it is more complicated. + // Current Chmod implementation on windows is not enough as it sets only + // FILE_ATTRIBUTE_READONLY throught SetFileAttributes call. But doing so does + // not affect directory access rights on older versions of Windows and Wine + // that we use to run autocheck tests. + // + // To get required access rights we use DACL in SetSecurityInfo. This is wrapped + // in RAII class that drops requested permissions on file/dir and grantss them + // back in destructor. + // + // Another obstacle is FILE_LIST_DIRECTORY permission when running on Wine. + // Dropping this permission is necessary to provoke error + // in GetFileInformationByHandle. Wine allows dropping this permission, but I + // have not found a way to grant it back. So tests crash during cleanup sequence. + // To make it possible to run this tests natively we detect Wine with special + // registry key and skip these tests only there. + +#ifdef _win_ + struct TLocalFree { + static void Destroy(void* ptr) { + LocalFree((HLOCAL)ptr); + } + }; + + bool IsWine() { + HKEY subKey = nullptr; + LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Wine", 0, KEY_READ, &subKey); + if (result == ERROR_SUCCESS) { + return true; + } + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, KEY_READ, &subKey); + if (result == ERROR_SUCCESS) { + return true; + } + + HMODULE hntdll = GetModuleHandle("ntdll.dll"); + if (!hntdll) { + return false; + } + + auto func = GetProcAddress(hntdll, "wine_get_version"); + return func != nullptr; + } + + class TWinFileDenyAccessScope { + public: + TWinFileDenyAccessScope(const TFsPath& name, DWORD permissions) + : Name_(name) + , Perms_(permissions) + { + DWORD res = 0; + PACL oldAcl = nullptr; + PSECURITY_DESCRIPTOR sd = nullptr; + + res = GetNamedSecurityInfoA((LPSTR)name.c_str(), + SE_FILE_OBJECT, + DACL_SECURITY_INFORMATION, + nullptr, + nullptr, + &oldAcl, + nullptr, + &sd); + SdHolder_.Reset(sd); + if (res != ERROR_SUCCESS) { + ythrow TSystemError(res) << "error in GetNamedSecurityInfoA"; + } + + Acl_ = SetAcl(oldAcl, DENY_ACCESS); + } + + ~TWinFileDenyAccessScope() { + try { + const TFsPath parent = Name_.Parent(); + Chmod(parent.c_str(), MODE0777); + Chmod(Name_.c_str(), MODE0777); + SetAcl((PACL)Acl_.Get(), GRANT_ACCESS); + } catch (const yexception& ex) { + Cerr << "~TWinFileDenyAccessScope failed: " << ex.AsStrBuf() << Endl; + } + } + + THolder SetAcl(PACL oldAcl, ACCESS_MODE accessMode) { + DWORD res = 0; + EXPLICIT_ACCESS ea; + PACL newAcl = nullptr; + THolder newAclHolder; + + memset(&ea, 0, sizeof(EXPLICIT_ACCESS)); + ea.grfAccessPermissions = Perms_; + ea.grfAccessMode = accessMode; + ea.grfInheritance = NO_INHERITANCE; + ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME; + ea.Trustee.ptstrName = (LPSTR) "CURRENT_USER"; + + res = SetEntriesInAcl(1, &ea, oldAcl, &newAcl); + newAclHolder.Reset(newAcl); + if (res != ERROR_SUCCESS) { + ythrow TSystemError(res) << "error in SetEntriesInAcl"; + } + + res = SetNamedSecurityInfoA((LPSTR)Name_.c_str(), + SE_FILE_OBJECT, + DACL_SECURITY_INFORMATION, + nullptr, + nullptr, + newAcl, + nullptr); + if (res != ERROR_SUCCESS) { + ythrow TSystemError(res) << "error in SetNamedSecurityInfoA"; + } + + return std::move(newAclHolder); + } + + private: + const TFsPath Name_; + const DWORD Perms_; + THolder SdHolder_; + THolder Acl_; + }; +#endif + + Y_UNIT_TEST(TestForceDeleteErrorUnlink) { + TTempDir tempDir; + + const TFsPath testDir = TFsPath(tempDir()).Child("dir"); + MakePathIfNotExist(testDir.c_str()); + + const TFsPath testFile = testDir.Child("file"); + { + TFixedBufferFileOutput out(testFile); + out << "data111!!!"; + } + +#ifdef _win_ + Chmod(testFile.c_str(), S_IRUSR); + Y_DEFER { + Chmod(testFile.c_str(), MODE0777); + }; +#else + Chmod(testDir.c_str(), S_IRUSR | S_IXUSR); + Y_DEFER { + Chmod(testDir.c_str(), MODE0777); + }; +#endif + + UNIT_ASSERT_EXCEPTION_CONTAINS(testFile.ForceDelete(), TIoException, "failed to delete"); + } + + Y_UNIT_TEST(TestForceDeleteErrorRmdir) { + TTempDir tempDir; + + const TFsPath testDir = TFsPath(tempDir()).Child("dir"); + const TFsPath testSubdir = testDir.Child("file"); + MakePathIfNotExist(testSubdir.c_str()); + +#ifdef _win_ + Chmod(testSubdir.c_str(), 0); + Y_DEFER { + Chmod(testSubdir.c_str(), MODE0777); + }; + TWinFileDenyAccessScope dirAcl(testDir, FILE_WRITE_DATA); +#else + Chmod(testDir.c_str(), S_IRUSR | S_IXUSR); + Y_DEFER { + Chmod(testDir.c_str(), MODE0777); + }; +#endif + + UNIT_ASSERT_EXCEPTION_CONTAINS(testSubdir.ForceDelete(), TIoException, "failed to delete"); + } + + Y_UNIT_TEST(TestForceDeleteErrorStatDir) { + TTempDir tempDir; + + const TFsPath testDir = TFsPath(tempDir()).Child("dir"); + const TFsPath testSubdir = testDir.Child("file"); + MakePathIfNotExist(testSubdir.c_str()); + +#ifdef _win_ + if (IsWine()) { + // FILE_LIST_DIRECTORY seem to be irreversible on wine + return; + } + TWinFileDenyAccessScope subdirAcl(testSubdir, FILE_READ_ATTRIBUTES); + TWinFileDenyAccessScope dirAcl(testDir, FILE_LIST_DIRECTORY); +#else + Chmod(testDir.c_str(), 0); + Y_DEFER { + Chmod(testDir.c_str(), MODE0777); + }; +#endif + + UNIT_ASSERT_EXCEPTION_CONTAINS(testSubdir.ForceDelete(), TIoException, "failed to stat"); + } + + Y_UNIT_TEST(TestForceDeleteErrorStatFile) { + TTempDir tempDir; + + const TFsPath testDir = TFsPath(tempDir()).Child("dir"); + MakePathIfNotExist(testDir.c_str()); + + const TFsPath testFile = testDir.Child("file"); + { + TFixedBufferFileOutput out(testFile); + out << "data111!!!"; + } + +#ifdef _win_ + if (IsWine()) { + // FILE_LIST_DIRECTORY seem to be irreversible on wine + return; + } + TWinFileDenyAccessScope fileAcl(testFile, FILE_READ_ATTRIBUTES); + TWinFileDenyAccessScope dirAcl(testDir, FILE_LIST_DIRECTORY); +#else + Chmod(testDir.c_str(), 0); + Y_DEFER { + Chmod(testDir.c_str(), MODE0777); + }; +#endif + + UNIT_ASSERT_EXCEPTION_CONTAINS(testFile.ForceDelete(), TIoException, "failed to stat"); + } + + Y_UNIT_TEST(TestForceDeleteErrorListDir) { + TTempDir tempDir; + + const TFsPath testDir = TFsPath(tempDir()).Child("dir"); + const TFsPath testSubdir = testDir.Child("file"); + MakePathIfNotExist(testSubdir.c_str()); + +#ifdef _win_ + if (IsWine()) { + // FILE_LIST_DIRECTORY seem to be irreversible on wine + return; + } + TWinFileDenyAccessScope subdirAcl(testSubdir, FILE_LIST_DIRECTORY); +#else + Chmod(testSubdir.c_str(), 0); + Y_DEFER { + Chmod(testSubdir.c_str(), MODE0777); + }; +#endif + + UNIT_ASSERT_EXCEPTION_CONTAINS(testSubdir.ForceDelete(), TIoException, "failed to opendir"); + } + +#ifdef _unix_ + Y_UNIT_TEST(TestForceDeleteErrorSymlink) { + TTempDir tempDir; + + const TFsPath testDir = TFsPath(tempDir()).Child("dir"); + MakePathIfNotExist(testDir.c_str()); + + const TFsPath testSymlink = testDir.Child("symlink"); + NFs::SymLink("something", testSymlink); + + Chmod(testSymlink.c_str(), S_IRUSR); + Chmod(testDir.c_str(), S_IRUSR | S_IXUSR); + Y_DEFER { + Chmod(testDir.c_str(), MODE0777); + Chmod(testSymlink.c_str(), MODE0777); + }; + + UNIT_ASSERT_EXCEPTION_CONTAINS(testSymlink.ForceDelete(), TIoException, "failed to delete"); + } +#endif + + Y_UNIT_TEST(TestCopyWithInitializedSplit) { + const TFsPath path1 = TFsPath("some_folder_with_file") / TFsPath("file_in_folder"); + path1.PathSplit(); + + const TFsPath path2 = path1; + const TPathSplit& split2 = path2.PathSplit(); + + for (const auto& it : split2) { + UNIT_ASSERT(path2.GetPath().begin() <= it.begin()); + UNIT_ASSERT(it.end() <= path2.GetPath().end()); + } + } + + Y_UNIT_TEST(TestAssignmentWithInitializedSplit) { + TFsPath path1 = TFsPath("some_folder_with_file_1") / TFsPath("file_in_folder_1"); + TFsPath path2 = TFsPath("some_folder_with_file_2") / TFsPath("file_in_folder_2"); + path1.PathSplit(); + path1 = path2; + UNIT_ASSERT_VALUES_EQUAL(path1.PathSplit().at(1), "file_in_folder_2"); + } + +#ifdef TSTRING_IS_STD_STRING + Y_UNIT_TEST(TestCopySplitSSO) { + // Summary length of path must be less minimal SSO length 19 bytes + constexpr TStringBuf A("a"); + constexpr TStringBuf B("b"); + constexpr TStringBuf C("c"); + for (auto constructorType = 0; constructorType < 2; ++constructorType) { + TFsPath path1 = TFsPath(A) / TFsPath(B); + const auto& split1 = path1.PathSplit(); + // Check split of path1 + UNIT_ASSERT_VALUES_EQUAL(split1.size(), 2); + UNIT_ASSERT_VALUES_EQUAL(split1.at(0), A); + UNIT_ASSERT_VALUES_EQUAL(split1.at(1), B); + TFsPath path2; + if (constructorType == 0) { // copy + path2 = TFsPath(path1); // copy constructor + } else if (constructorType == 1) { // move + path2 = TFsPath(std::move(path1)); // move constructor + } + const auto& split2 = path2.PathSplit(); + path1 = TFsPath(C); // invalidate previous Path_ in path1 + const auto& newsplit1 = path1.PathSplit(); + // Check that split of path1 was overwrited (invalidate previous TStringBuf) + UNIT_ASSERT_VALUES_EQUAL(newsplit1.size(), 1); + UNIT_ASSERT_VALUES_EQUAL(newsplit1.at(0), C); + // Check split of path2 without segfault + UNIT_ASSERT_VALUES_EQUAL(split2.size(), 2); + UNIT_ASSERT_VALUES_EQUAL(split2.at(0), A); + UNIT_ASSERT_VALUES_EQUAL(split2.at(1), B); + } + } +#endif + + Y_UNIT_TEST(TestCopySplitNoneSSO) { + // Lenght of directory name must overhead SSO length 19-23 bytes + const TString DIR_A = TString("Dir") + TString(32, 'A'); + const TString DIR_B = TString("Dir") + TString(64, 'B'); + const TString DIR_C = TString("Dir") + TString(128, 'C'); + for (auto constructorType = 0; constructorType < 2; ++constructorType) { + TFsPath path1 = TFsPath(DIR_A) / TFsPath(DIR_B); + auto& split1 = path1.PathSplit(); + // Check split of path1 + UNIT_ASSERT_VALUES_EQUAL(split1.size(), 2); + UNIT_ASSERT_VALUES_EQUAL(split1.at(0), DIR_A); + UNIT_ASSERT_VALUES_EQUAL(split1.at(1), DIR_B); + TFsPath path2; + if (constructorType == 0) { // copy + path2 = TFsPath(path1); // copy constructor + } else if (constructorType == 1) { // move + path2 = TFsPath(std::move(path1)); // move constructor + } + const auto& split2 = path2.PathSplit(); + path1 = TFsPath(DIR_C); // invalidate previous Path_ in path1 + const auto& newsplit1 = path1.PathSplit(); + // Check that split of path1 was overwrited (invalidate previous TStringBuf) + UNIT_ASSERT_VALUES_EQUAL(newsplit1.size(), 1); + UNIT_ASSERT_VALUES_EQUAL(newsplit1.at(0), DIR_C); + // Check split of path2 without segfault + UNIT_ASSERT_VALUES_EQUAL(split2.size(), 2); + UNIT_ASSERT_VALUES_EQUAL(split2.at(0), DIR_A); + UNIT_ASSERT_VALUES_EQUAL(split2.at(1), DIR_B); + } + } +} diff --git a/util/folder/path_ut.pyx b/util/folder/path_ut.pyx new file mode 100644 index 00000000000..be8d3ce35af --- /dev/null +++ b/util/folder/path_ut.pyx @@ -0,0 +1,380 @@ +# cython: c_string_type=str, c_string_encoding=utf8 + +from util.folder.path cimport TFsPath +from util.generic.string cimport TString, TStringBuf +from util.generic.vector cimport TVector + +import unittest +import yatest.common + +import os.path + + +class TestPath(unittest.TestCase): + def test_ctor1(self): + cdef TFsPath path = TFsPath() + self.assertEqual(path.IsDefined(), False) + self.assertEqual(path.c_str(), "") + + def test_ctor2(self): + cdef TString str_path = "/a/b/c" + cdef TFsPath path = TFsPath(str_path) + self.assertEqual(path.IsDefined(), True) + self.assertEqual(path.c_str(), "/a/b/c") + + def test_ctor3(self): + cdef TStringBuf buf_path = "/a/b/c" + cdef TFsPath path = TFsPath(buf_path) + self.assertEqual(path.IsDefined(), True) + self.assertEqual(path.c_str(), "/a/b/c") + + def test_ctor4(self): + cdef char* char_path = "/a/b/c" + cdef TFsPath path = TFsPath(char_path) + self.assertEqual(path.IsDefined(), True) + self.assertEqual(path.c_str(), "/a/b/c") + + def test_assignment(self): + cdef TFsPath path1 = TFsPath("/a/b") + cdef TFsPath path2 = TFsPath("/a/c") + + self.assertEqual(path1.GetPath(), "/a/b") + self.assertEqual(path2.GetPath(), "/a/c") + + path2 = path1 + + self.assertEqual(path1.GetPath(), "/a/b") + self.assertEqual(path2.GetPath(), "/a/b") + + def test_check_defined(self): + cdef TFsPath path1 = TFsPath() + with self.assertRaises(RuntimeError): + path1.CheckDefined() + self.assertEqual(path1.IsDefined(), False) + if path1: + assert False + else: + pass + + cdef TFsPath path2 = TFsPath("") + with self.assertRaises(RuntimeError): + path2.CheckDefined() + self.assertEqual(path2.IsDefined(), False) + if path2: + assert False + else: + pass + + cdef TFsPath path3 = TFsPath("/") + path3.CheckDefined() + self.assertEqual(path3.IsDefined(), True) + if path3: + pass + else: + assert False + + def test_comparison(self): + cdef TFsPath path1 = TFsPath("/a/b") + cdef TFsPath path2 = TFsPath("/a/c") + cdef TFsPath path3 = TFsPath("/a/b") + + self.assertEqual(path1 == path3, True) + self.assertEqual(path1 != path2, True) + self.assertEqual(path3 != path2, True) + + def test_concatenation(self): + cdef TFsPath path1 = TFsPath("/a") + cdef TFsPath path2 = TFsPath("b") + cdef TFsPath path3 = path1 / path2 + cdef TFsPath path4 = TFsPath("/a/b") + + self.assertEqual(path3 == path4, True) + + def test_fix(self): + cdef TFsPath path = TFsPath("test_fix/b/c/../d") + cdef TFsPath fixed = path.Fix() + self.assertEqual(fixed.GetPath(), "test_fix/b/d") + + def test_parts(self): + cdef TFsPath path = TFsPath("/a/b/c") + self.assertEqual(path.GetPath(), "/a/b/c") + self.assertEqual(path.GetName(), "c") + self.assertEqual(path.GetExtension(), "") + self.assertEqual(path.Basename(), "c") + self.assertEqual(path.Dirname(), "/a/b") + + cdef TFsPath path_ext = TFsPath("/a/b/c.ext") + self.assertEqual(path_ext.GetPath(), "/a/b/c.ext") + self.assertEqual(path_ext.GetName(), "c.ext") + self.assertEqual(path_ext.GetExtension(), "ext") + self.assertEqual(path_ext.Basename(), "c.ext") + self.assertEqual(path_ext.Dirname(), "/a/b") + + cdef TFsPath path_only_ext = TFsPath("/a/b/.ext") + self.assertEqual(path_only_ext.GetPath(), "/a/b/.ext") + self.assertEqual(path_only_ext.GetName(), ".ext") + self.assertEqual(path_only_ext.GetExtension(), "") + self.assertEqual(path_only_ext.Basename(), ".ext") + self.assertEqual(path_only_ext.Dirname(), "/a/b") + + cdef TFsPath path_dir = TFsPath("/a/b/") + self.assertEqual(path_dir.GetPath(), "/a/b/") + self.assertEqual(path_dir.GetName(), "b") + self.assertEqual(path_dir.GetExtension(), "") + self.assertEqual(path_dir.Basename(), "b") + self.assertEqual(path_dir.Dirname(), "/a") + + def test_absolute(self): + cdef TFsPath path_absolute = TFsPath("/a/b/c") + self.assertEqual(path_absolute.IsAbsolute(), True) + self.assertEqual(path_absolute.IsRelative(), False) + + self.assertEqual(path_absolute.IsSubpathOf(TFsPath("/a/b")), True) + self.assertEqual(path_absolute.IsNonStrictSubpathOf(TFsPath("/a/b")), True) + self.assertEqual(TFsPath("/a/b").IsContainerOf(path_absolute), True) + + self.assertEqual(path_absolute.IsSubpathOf(TFsPath("/a/b/c")), False) + self.assertEqual(path_absolute.IsNonStrictSubpathOf(TFsPath("/a/b/c")), True) + self.assertEqual(TFsPath("/a/b/c").IsContainerOf(path_absolute), False) + + self.assertEqual(path_absolute.IsSubpathOf(TFsPath("/a/c")), False) + self.assertEqual(path_absolute.IsNonStrictSubpathOf(TFsPath("/a/c")), False) + self.assertEqual(TFsPath("/a/c").IsContainerOf(path_absolute), False) + + with self.assertRaises(RuntimeError): + path_absolute.RelativeTo(TFsPath("/a/c")) + self.assertEqual(path_absolute.RelativePath(TFsPath("/a/с")).GetPath(), "../b/c") + self.assertEqual(path_absolute.RelativeTo(TFsPath("/a")).GetPath(), "b/c") + self.assertEqual(path_absolute.RelativePath(TFsPath("/a")).GetPath(), "b/c") + self.assertEqual(path_absolute.RelativeTo(TFsPath("/")).GetPath(), "a/b/c") + self.assertEqual(path_absolute.RelativePath(TFsPath("/")).GetPath(), "a/b/c") + + with self.assertRaises(RuntimeError): + path_absolute.RelativeTo(TFsPath("./a")) + with self.assertRaises(RuntimeError): + path_absolute.RelativePath(TFsPath("d")) + self.assertEqual(path_absolute.RelativePath(TFsPath("./a")).GetPath(), "b/c") + + self.assertEqual(path_absolute.Parent().GetPath(), "/a/b") + self.assertEqual(path_absolute.Child("d").GetPath(), "/a/b/c/d") + + def test_relative(self): + cdef TFsPath path_relative_1 = TFsPath("a/b/c") + self.assertEqual(path_relative_1.IsAbsolute(), False) + self.assertEqual(path_relative_1.IsRelative(), True) + + self.assertEqual(path_relative_1.IsSubpathOf(TFsPath("a/b")), True) + self.assertEqual(path_relative_1.IsNonStrictSubpathOf(TFsPath("a/b")), True) + self.assertEqual(TFsPath("a/b").IsContainerOf(path_relative_1), True) + + self.assertEqual(path_relative_1.IsSubpathOf(TFsPath("a/b/c")), False) + self.assertEqual(path_relative_1.IsNonStrictSubpathOf(TFsPath("a/b/c")), True) + self.assertEqual(TFsPath("a/b/c").IsContainerOf(path_relative_1), False) + + self.assertEqual(path_relative_1.IsSubpathOf(TFsPath("a/c")), False) + self.assertEqual(path_relative_1.IsNonStrictSubpathOf(TFsPath("a/c")), False) + self.assertEqual(TFsPath("a/c").IsContainerOf(path_relative_1), False) + + self.assertEqual(path_relative_1.Parent().GetPath(), "a/b") + self.assertEqual(path_relative_1.Child("d").GetPath(), "a/b/c/d") + + cdef TFsPath path_relative_2 = TFsPath("./a/b/c") + self.assertEqual(path_relative_2.IsAbsolute(), False) + self.assertEqual(path_relative_2.IsRelative(), True) + + self.assertEqual(path_relative_2.IsSubpathOf(TFsPath("a/b")), True) + self.assertEqual(path_relative_2.IsNonStrictSubpathOf(TFsPath("a/b")), True) + self.assertEqual(TFsPath("a/b").IsContainerOf(path_relative_2), True) + + self.assertEqual(path_relative_2.IsSubpathOf(TFsPath("a/b/c")), False) + self.assertEqual(path_relative_2.IsNonStrictSubpathOf(TFsPath("a/b/c")), True) + self.assertEqual(TFsPath("a/b/c").IsContainerOf(path_relative_2), False) + + self.assertEqual(path_relative_2.IsSubpathOf(TFsPath("a/c")), False) + self.assertEqual(path_relative_2.IsNonStrictSubpathOf(TFsPath("a/c")), False) + self.assertEqual(TFsPath("a/c").IsContainerOf(path_relative_2), False) + + with self.assertRaises(RuntimeError): + path_relative_2.RelativeTo(TFsPath("a/c")) + self.assertEqual(path_relative_2.RelativePath(TFsPath("a/с")).GetPath(), "../b/c") + self.assertEqual(path_relative_2.RelativeTo(TFsPath("a")).GetPath(), "b/c") + self.assertEqual(path_relative_2.RelativePath(TFsPath("a")).GetPath(), "b/c") + self.assertEqual(path_relative_2.RelativeTo(TFsPath("./")).GetPath(), "a/b/c") + self.assertEqual(path_relative_2.RelativePath(TFsPath("/a")).GetPath(), "b/c") + + with self.assertRaises(RuntimeError): + self.assertEqual(path_relative_2.RelativePath(TFsPath("./")).GetPath(), "a/b/c") + + with self.assertRaises(RuntimeError): + path_relative_2.RelativeTo(TFsPath("/d")) + with self.assertRaises(RuntimeError): + path_relative_2.RelativePath(TFsPath("/d")) + with self.assertRaises(RuntimeError): + path_relative_2.RelativePath(TFsPath("/")) + + self.assertEqual(path_relative_2.Parent().GetPath(), "a/b") + self.assertEqual(path_relative_2.Child("d").GetPath(), "a/b/c/d") + + def test_mkdir(self): + cdef TFsPath directory = TFsPath("test_mkdir") + cdef TFsPath full = directory / directory + cdef TFsPath internal = full / directory + with self.assertRaises(RuntimeError): + full.MkDir() + full.MkDirs() + internal.MkDir() + + def test_list(self): + cdef TFsPath dir = TFsPath("test_list") + dir.MkDir() + TFsPath("test_list/b").Touch() + TFsPath("test_list/c").Touch() + + cdef TVector[TFsPath] files + cdef TVector[TString] names + + dir.List(files) + dir.ListNames(names) + + self.assertEqual(files.size(), 2) + self.assertEqual(sorted([files[0].GetPath(), files[1].GetPath()]), ["test_list/b", "test_list/c"]) + self.assertEqual(names.size(), 2) + self.assertEqual(sorted(list(names)), ["b", "c"]) + + def test_contains(self): + cdef TFsPath path = TFsPath("a/b/c") + self.assertEqual(path.Contains("c"), True) + self.assertEqual(path.Contains("b"), True) + self.assertEqual(path.Contains("d"), False) + + def test_delete(self): + cdef TFsPath root = TFsPath("/") + with self.assertRaises(RuntimeError): + root.DeleteIfExists() + with self.assertRaises(RuntimeError): + root.ForceDelete() + + cdef TFsPath directory = TFsPath("test_delete") + cdef TFsPath full = directory / directory + full.MkDirs() + + self.assertEqual(full.Exists(), True) + with self.assertRaises(RuntimeError): + directory.DeleteIfExists() + self.assertEqual(directory.Exists(), True) + directory.ForceDelete() + self.assertEqual(directory.Exists(), False) + + cdef TFsPath local_file = TFsPath("test_delete_1") + self.assertEqual(local_file.Exists(), False) + local_file.DeleteIfExists() + self.assertEqual(local_file.Exists(), False) + local_file.ForceDelete() + self.assertEqual(local_file.Exists(), False) + + local_file.Touch() + self.assertEqual(local_file.Exists(), True) + local_file.DeleteIfExists() + self.assertEqual(local_file.Exists(), False) + + local_file.Touch() + self.assertEqual(local_file.Exists(), True) + local_file.ForceDelete() + self.assertEqual(local_file.Exists(), False) + + full.MkDirs() + self.assertEqual(full.Exists(), True) + full.DeleteIfExists() + self.assertEqual(full.Exists(), False) + self.assertEqual(directory.Exists(), True) + directory.DeleteIfExists() + self.assertEqual(directory.Exists(), False) + + def test_checks(self): + cdef TFsPath local_file = TFsPath("test_checks") + with self.assertRaises(RuntimeError): + local_file.CheckExists() + local_file.Touch() + self.assertEqual(local_file.Exists(), True) + self.assertEqual(local_file.IsDirectory(), False) + self.assertEqual(local_file.IsFile(), True) + self.assertEqual(local_file.IsSymlink(), False) + local_file.CheckExists() + + local_file.DeleteIfExists() + local_file.MkDir() + self.assertEqual(local_file.Exists(), True) + self.assertEqual(local_file.IsDirectory(), True) + self.assertEqual(local_file.IsFile(), False) + self.assertEqual(local_file.IsSymlink(), False) + local_file.CheckExists() + + def test_rename(self): + cdef TFsPath path = TFsPath("test_rename_a") + path.Touch() + + cdef TString path_str = "test_rename_b" + cdef TFsPath path_from_str = TFsPath(path_str) + self.assertEqual(path.Exists(), True) + self.assertEqual(path_from_str.Exists(), False) + path.RenameTo(path_str) + self.assertEqual(path.Exists(), False) + self.assertEqual(path_from_str.Exists(), True) + + cdef const char* path_char = "test_rename_c" + cdef TFsPath path_from_char = TFsPath(path_char) + self.assertEqual(path_from_str.Exists(), True) + self.assertEqual(path_from_char.Exists(), False) + path_from_str.RenameTo(path_char) + self.assertEqual(path_from_str.Exists(), False) + self.assertEqual(path_from_char.Exists(), True) + + path_from_char.RenameTo(path) + + self.assertEqual(path_from_char.Exists(), False) + self.assertEqual(path.Exists(), True) + + path.ForceRenameTo(path_str) + + self.assertEqual(path_from_str.Exists(), True) + self.assertEqual(path.Exists(), False) + + with self.assertRaises(RuntimeError): + path_from_str.RenameTo("") + + def test_copy(self): + cdef TString dst = "test_copy_dst" + cdef TFsPath src_path = TFsPath("test_copy_src") + cdef TFsPath dst_path = TFsPath(dst) + self.assertEqual(src_path.Exists(), False) + src_path.Touch() + self.assertEqual(src_path.Exists(), True) + src_path.CopyTo(dst, False) + self.assertEqual(src_path.Exists(), True) + self.assertEqual(dst_path.Exists(), True) + + def test_real_path(self): + cdef TFsPath path = TFsPath("test_real_path_a") + path.Touch() + real_work_path = os.path.join(os.path.realpath(yatest.common.work_path()), "test_real_path_a") + self.assertEqual(path.RealPath().GetPath(), real_work_path) + self.assertEqual(path.RealLocation().GetPath(), real_work_path) + with self.assertRaises(RuntimeError): + path.ReadLink() + + def test_cwd(self): + cdef TFsPath path = TFsPath.Cwd() + self.assertEqual(path.GetPath(), os.path.realpath(yatest.common.work_path())) + + def test_swap(self): + cdef TFsPath first = TFsPath("first") + cdef TFsPath second = TFsPath("second") + + self.assertEqual(first.GetPath(), "first") + self.assertEqual(second.GetPath(), "second") + first.Swap(second) + self.assertEqual(first.GetPath(), "second") + self.assertEqual(second.GetPath(), "first") + second.Swap(first) + self.assertEqual(first.GetPath(), "first") + self.assertEqual(second.GetPath(), "second") diff --git a/util/folder/pathsplit.cpp b/util/folder/pathsplit.cpp new file mode 100644 index 00000000000..816f6960245 --- /dev/null +++ b/util/folder/pathsplit.cpp @@ -0,0 +1,149 @@ +#include "pathsplit.h" + +#include +#include + +template +static inline size_t ToReserve(const T& t) { + size_t ret = t.size() + 5; + + for (auto it = t.begin(); it != t.end(); ++it) { + ret += it->size(); + } + + return ret; +} + +void TPathSplitTraitsUnix::DoParseFirstPart(const TStringBuf part) { + if (part == TStringBuf(".")) { + push_back(TStringBuf(".")); + + return; + } + + if (IsAbsolutePath(part)) { + IsAbsolute = true; + } + + DoParsePart(part); +} + +void TPathSplitTraitsUnix::DoParsePart(const TStringBuf part0) { + DoAppendHint(part0.size() / 8); + + TStringBuf next(part0); + TStringBuf part; + + while (TStringBuf(next).TrySplit('/', part, next)) { + AppendComponent(part); + } + + AppendComponent(next); +} + +void TPathSplitTraitsWindows::DoParseFirstPart(const TStringBuf part0) { + TStringBuf part(part0); + + if (part == TStringBuf(".")) { + push_back(TStringBuf(".")); + + return; + } + + if (IsAbsolutePath(part)) { + IsAbsolute = true; + + if (part.size() > 1 && part[1] == ':') { + Drive = part.SubStr(0, 2); + part = part.SubStr(2); + } + } + + DoParsePart(part); +} + +void TPathSplitTraitsWindows::DoParsePart(const TStringBuf part0) { + DoAppendHint(part0.size() / 8); + + size_t pos = 0; + TStringBuf part(part0); + + while (pos < part.size()) { + while (pos < part.size() && this->IsPathSep(part[pos])) { + ++pos; + } + + const char* begin = part.data() + pos; + + while (pos < part.size() && !this->IsPathSep(part[pos])) { + ++pos; + } + + AppendComponent(TStringBuf(begin, part.data() + pos)); + } +} + +TString TPathSplitStore::DoReconstruct(const TStringBuf slash) const { + TString r; + + r.reserve(ToReserve(*this)); + + if (IsAbsolute) { + r.AppendNoAlias(Drive); + r.AppendNoAlias(slash); + } + + for (auto i = begin(); i != end(); ++i) { + if (i != begin()) { + r.AppendNoAlias(slash); + } + + r.AppendNoAlias(*i); + } + + return r; +} + +void TPathSplitStore::AppendComponent(const TStringBuf comp) { + if (!comp || comp == TStringBuf(".")) { + // ignore + } else if (comp == TStringBuf("..") && !empty() && back() != TStringBuf("..")) { + pop_back(); + } else { + // push back first .. also + push_back(comp); + } +} + +TStringBuf TPathSplitStore::Extension() const { + return size() > 0 ? CutExtension(back()) : TStringBuf(); +} + +template <> +void Out(IOutputStream& o, const TPathSplit& ps) { + o << ps.Reconstruct(); +} + +TString JoinPaths(const TPathSplit& p1, const TPathSplit& p2) { + if (p2.IsAbsolute) { + ythrow yexception() << "can not join " << p1 << " and " << p2; + } + + return TPathSplit(p1).AppendMany(p2.begin(), p2.end()).Reconstruct(); +} + +TStringBuf CutExtension(const TStringBuf fileName) { + if (fileName.empty()) { + return fileName; + } + + TStringBuf name; + TStringBuf extension; + fileName.RSplit('.', name, extension); + if (name.empty()) { + // dot at a start or not found + return name; + } else { + return extension; + } +} diff --git a/util/folder/pathsplit.h b/util/folder/pathsplit.h new file mode 100644 index 00000000000..d134338e35d --- /dev/null +++ b/util/folder/pathsplit.h @@ -0,0 +1,113 @@ +#pragma once + +#include +#include +#include +#include + +//do not own any data +struct TPathSplitStore: public TVector { + TStringBuf Drive; + bool IsAbsolute = false; + + void AppendComponent(const TStringBuf comp); + TStringBuf Extension() const; + +protected: + TString DoReconstruct(const TStringBuf slash) const; + + inline void DoAppendHint(size_t hint) { + reserve(size() + hint); + } +}; + +struct TPathSplitTraitsUnix: public TPathSplitStore { + static constexpr char MainPathSep = '/'; + + inline TString Reconstruct() const { + return DoReconstruct(TStringBuf("/")); + } + + static constexpr bool IsPathSep(const char c) noexcept { + return c == '/'; + } + + static inline bool IsAbsolutePath(const TStringBuf path) noexcept { + return path && IsPathSep(path[0]); + } + + void DoParseFirstPart(const TStringBuf part); + void DoParsePart(const TStringBuf part); +}; + +struct TPathSplitTraitsWindows: public TPathSplitStore { + static constexpr char MainPathSep = '\\'; + + inline TString Reconstruct() const { + return DoReconstruct(TStringBuf("\\")); + } + + static constexpr bool IsPathSep(char c) noexcept { + return c == '/' || c == '\\'; + } + + static inline bool IsAbsolutePath(const TStringBuf path) noexcept { + return path && (IsPathSep(path[0]) || (path.size() > 1 && path[1] == ':' && IsAsciiAlpha(path[0]) && (path.size() == 2 || IsPathSep(path[2])))); + } + + void DoParseFirstPart(const TStringBuf part); + void DoParsePart(const TStringBuf part); +}; + +#if defined(_unix_) +using TPathSplitTraitsLocal = TPathSplitTraitsUnix; +#else +using TPathSplitTraitsLocal = TPathSplitTraitsWindows; +#endif + +template +class TPathSplitBase: public TTraits { +public: + inline TPathSplitBase() = default; + + inline TPathSplitBase(const TStringBuf part) { + this->ParseFirstPart(part); + } + + inline TPathSplitBase& AppendHint(size_t hint) { + this->DoAppendHint(hint); + + return *this; + } + + inline TPathSplitBase& ParseFirstPart(const TStringBuf part) { + this->DoParseFirstPart(part); + + return *this; + } + + inline TPathSplitBase& ParsePart(const TStringBuf part) { + this->DoParsePart(part); + + return *this; + } + + template + inline TPathSplitBase& AppendMany(It b, It e) { + this->AppendHint(e - b); + + while (b != e) { + this->AppendComponent(*b++); + } + + return *this; + } +}; + +using TPathSplit = TPathSplitBase; +using TPathSplitUnix = TPathSplitBase; +using TPathSplitWindows = TPathSplitBase; + +TString JoinPaths(const TPathSplit& p1, const TPathSplit& p2); + +TStringBuf CutExtension(const TStringBuf fileName); diff --git a/util/folder/pathsplit_ut.cpp b/util/folder/pathsplit_ut.cpp new file mode 100644 index 00000000000..1310ba15d08 --- /dev/null +++ b/util/folder/pathsplit_ut.cpp @@ -0,0 +1,482 @@ +// File includes itself to make multiple passes of its suites with different platform-dependent definitions + +#ifndef PS_INCLUDED +// Outer part + + #include "pathsplit.h" + + #include + + #define VAR(NAME) Y_CAT(NAME, __LINE__) + + #define PS_CHECK(input, ...) \ + const char* VAR(model)[] = {"", __VA_ARGS__}; \ + UNIT_ASSERT_EQUAL(input.size(), sizeof(VAR(model)) / sizeof(const char*) - 1); \ + for (size_t n = 0; n < input.size(); ++n) { \ + UNIT_ASSERT_STRINGS_EQUAL(input[n], VAR(model)[n + 1]); \ + } + + #define PS_INCLUDED + + #define PSUF(NAME) NAME + #define PSUF_LOCAL(NAME) NAME##Local + #include + #undef PSUF + #undef PSUF_LOCAL + + #define PSUF(NAME) NAME##Unix + #define PSUF_LOCAL(NAME) PSUF(NAME) + #ifdef _win_ + #undef _win_ + #define REVERT_WIN + #endif + #include + #ifdef REVERT_WIN + #define _win_ + #undef REVERT_WIN + #endif + #undef PSUF + #undef PSUF_LOCAL + + #define PSUF(NAME) NAME##Windows + #define PSUF_LOCAL(NAME) PSUF(NAME) + #ifndef _win_ + #define _win_ + #define REVERT_WIN + #endif + #include + #ifdef REVERT_WIN + #undef _win_ + #undef REVERT_WIN + #endif + #undef PSUF + #undef PSUF_LOCAL + + #undef PS_INCLUDED + +#else +// Inner part + + #ifdef _win_ + #define TRUE_ONLY_WIN true + #else + #define TRUE_ONLY_WIN false + #endif + +Y_UNIT_TEST_SUITE(PSUF(PathSplit)) { + Y_UNIT_TEST(Empty) { + PSUF(TPathSplit) + ps; + PS_CHECK(ps); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + } + + Y_UNIT_TEST(Relative) { + PSUF(TPathSplit) + ps("some/usual/path"); + PS_CHECK(ps, "some", "usual", "path"); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + } + + Y_UNIT_TEST(Absolute) { + PSUF(TPathSplit) + ps("/some/usual/path"); + PS_CHECK(ps, "some", "usual", "path"); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, true); + } + + Y_UNIT_TEST(Self) { + PSUF(TPathSplit) + ps("."); + PS_CHECK(ps, "."); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + } + + Y_UNIT_TEST(Parent) { + PSUF(TPathSplit) + ps(".."); + PS_CHECK(ps, ".."); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + } + + Y_UNIT_TEST(Root) { + PSUF(TPathSplit) + ps("/"); + PS_CHECK(ps); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, true); + } + + Y_UNIT_TEST(Reconstruct) { + PSUF(TPathSplit) + ps("some/usual/path/../../other/././//path"); + #ifdef _win_ + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "some\\other\\path"); + #else + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "some/other/path"); + #endif + + ps = PSUF(TPathSplit)("/some/usual/path/../../other/././//path"); + #ifdef _win_ + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "\\some\\other\\path"); + #else + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "/some/other/path"); + #endif + } + + Y_UNIT_TEST(ParseFirstPart) { + PSUF(TPathSplit) + ps; + ps.ParseFirstPart("some/usual/path"); + PS_CHECK(ps, "some", "usual", "path"); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + + ps = PSUF(TPathSplit)(); + ps.ParseFirstPart("/some/usual/path"); + PS_CHECK(ps, "some", "usual", "path"); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, true); + } + + Y_UNIT_TEST(ParsePart) { + PSUF(TPathSplit) + ps("some/usual/path"); + ps.ParsePart("sub/path"); + PS_CHECK(ps, "some", "usual", "path", "sub", "path"); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + + ps = PSUF(TPathSplit)("some/usual/path"); + ps.ParsePart("/sub/path"); + PS_CHECK(ps, "some", "usual", "path", "sub", "path"); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + } + + Y_UNIT_TEST(ParsePartSelf) { + PSUF(TPathSplit) + ps("some/usual/path"); + ps.ParsePart("."); + PS_CHECK(ps, "some", "usual", "path"); + + ps = PSUF(TPathSplit)("some/usual/path"); + ps.ParsePart("././."); + PS_CHECK(ps, "some", "usual", "path"); + } + + Y_UNIT_TEST(ParsePartParent) { + PSUF(TPathSplit) + ps("some/usual/path"); + ps.ParsePart(".."); + PS_CHECK(ps, "some", "usual"); + + ps = PSUF(TPathSplit)("some/usual/path"); + ps.ParsePart("../.."); + PS_CHECK(ps, "some"); + + ps = PSUF(TPathSplit)("some/usual/path"); + ps.ParsePart("../../.."); + PS_CHECK(ps); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + + ps = PSUF(TPathSplit)("/some/usual/path"); + ps.ParsePart("../../.."); + PS_CHECK(ps); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, true); + } + + Y_UNIT_TEST(ParsePartOverflow) { + PSUF(TPathSplit) + ps("some/usual/path"); + ps.ParsePart("../../../../.."); + PS_CHECK(ps, "..", ".."); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + + ps = PSUF(TPathSplit)("/some/usual/path"); + ps.ParsePart("../../../../.."); + PS_CHECK(ps, "..", ".."); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, true); + } + + Y_UNIT_TEST(WinRelative) { + PSUF(TPathSplit) + ps("some\\usual\\path"); + #ifdef _win_ + PS_CHECK(ps, "some", "usual", "path"); + #else + PS_CHECK(ps, "some\\usual\\path"); + #endif + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + } + + Y_UNIT_TEST(WinAbsolute) { + PSUF(TPathSplit) + ps("\\some\\usual\\path"); + #ifdef _win_ + PS_CHECK(ps, "some", "usual", "path"); + #else + PS_CHECK(ps, "\\some\\usual\\path"); + #endif + UNIT_ASSERT_EQUAL(ps.IsAbsolute, TRUE_ONLY_WIN); + + PSUF(TPathSplit) + psDrive("C:\\some\\usual\\path"); + #ifdef _win_ + PS_CHECK(psDrive, "some", "usual", "path"); + UNIT_ASSERT_EQUAL(psDrive.Drive, "C:"); + #else + PS_CHECK(psDrive, "C:\\some\\usual\\path"); + #endif + UNIT_ASSERT_EQUAL(psDrive.IsAbsolute, TRUE_ONLY_WIN); + + PSUF(TPathSplit) + psDrive2("C:/some/usual/path"); + #ifdef _win_ + PS_CHECK(psDrive2, "some", "usual", "path"); + UNIT_ASSERT_EQUAL(psDrive2.Drive, "C:"); + #else + PS_CHECK(psDrive2, "C:", "some", "usual", "path"); + #endif + UNIT_ASSERT_EQUAL(psDrive2.IsAbsolute, TRUE_ONLY_WIN); + } + + Y_UNIT_TEST(WinRoot) { + PSUF(TPathSplit) + ps("\\"); + #ifdef _win_ + PS_CHECK(ps); + #else + PS_CHECK(ps, "\\"); + #endif + UNIT_ASSERT_EQUAL(ps.IsAbsolute, TRUE_ONLY_WIN); + + PSUF(TPathSplit) + psDrive("C:"); + #ifdef _win_ + PS_CHECK(psDrive); + UNIT_ASSERT_EQUAL(psDrive.Drive, "C:"); + #else + PS_CHECK(psDrive, "C:"); + #endif + UNIT_ASSERT_EQUAL(psDrive.IsAbsolute, TRUE_ONLY_WIN); + } + + Y_UNIT_TEST(WinReconstruct) { + PSUF(TPathSplit) + ps("some\\usual\\path\\..\\..\\other\\.\\.\\\\\\path"); + #ifdef _win_ + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "some\\other\\path"); + #else + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "some\\usual\\path\\..\\..\\other\\.\\.\\\\\\path"); + #endif + + ps = PSUF(TPathSplit)("\\some\\usual\\path\\..\\..\\other\\.\\.\\\\\\path"); + #ifdef _win_ + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "\\some\\other\\path"); + #else + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "\\some\\usual\\path\\..\\..\\other\\.\\.\\\\\\path"); + #endif + } + + Y_UNIT_TEST(WinParseFirstPart) { + PSUF(TPathSplit) + ps; + ps.ParseFirstPart("some\\usual\\path"); + #ifdef _win_ + PS_CHECK(ps, "some", "usual", "path"); + #else + PS_CHECK(ps, "some\\usual\\path"); + #endif + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + + ps = PSUF(TPathSplit)(); + ps.ParseFirstPart("\\some\\usual\\path"); + #ifdef _win_ + PS_CHECK(ps, "some", "usual", "path"); + #else + PS_CHECK(ps, "\\some\\usual\\path"); + #endif + UNIT_ASSERT_EQUAL(ps.IsAbsolute, TRUE_ONLY_WIN); + } + + Y_UNIT_TEST(WinParsePart) { + PSUF(TPathSplit) + ps("some\\usual\\path"); + ps.ParsePart("sub\\path"); + #ifdef _win_ + PS_CHECK(ps, "some", "usual", "path", "sub", "path"); + #else + PS_CHECK(ps, "some\\usual\\path", "sub\\path"); + #endif + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + + ps = PSUF(TPathSplit)("some\\usual\\path"); + ps.ParsePart("\\sub\\path"); + #ifdef _win_ + PS_CHECK(ps, "some", "usual", "path", "sub", "path"); + #else + PS_CHECK(ps, "some\\usual\\path", "\\sub\\path"); + #endif + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + } + + #ifdef _win_ + Y_UNIT_TEST(WinParsePartSelf) { + PSUF(TPathSplit) + ps("some\\usual\\path"); + ps.ParsePart("."); + PS_CHECK(ps, "some", "usual", "path"); + + ps = PSUF(TPathSplit)("some\\usual\\path"); + ps.ParsePart(".\\.\\."); + PS_CHECK(ps, "some", "usual", "path"); + } + + Y_UNIT_TEST(WinParsePartParent) { + PSUF(TPathSplit) + ps("some\\usual\\path"); + ps.ParsePart(".."); + PS_CHECK(ps, "some", "usual"); + + ps = PSUF(TPathSplit)("some\\usual\\path"); + ps.ParsePart("..\\.."); + PS_CHECK(ps, "some"); + + ps = PSUF(TPathSplit)("some\\usual\\path"); + ps.ParsePart("..\\..\\.."); + PS_CHECK(ps); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + + ps = PSUF(TPathSplit)("\\some\\usual\\path"); + ps.ParsePart("..\\..\\.."); + PS_CHECK(ps); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, true); + + ps = PSUF(TPathSplit)("C:\\some\\usual\\path"); + ps.ParsePart("..\\..\\.."); + PS_CHECK(ps); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, true); + UNIT_ASSERT_EQUAL(ps.Drive, "C:"); + } + + Y_UNIT_TEST(WinParsePartOverflow) { + PSUF(TPathSplit) + ps("some\\usual\\path"); + ps.ParsePart("..\\..\\..\\..\\.."); + PS_CHECK(ps, "..", ".."); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + + ps = PSUF(TPathSplit)("\\some\\usual\\path"); + ps.ParsePart("..\\..\\..\\..\\.."); + PS_CHECK(ps, "..", ".."); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, true); + + ps = PSUF(TPathSplit)("C:\\some\\usual\\path"); + ps.ParsePart("..\\..\\..\\..\\.."); + PS_CHECK(ps, "..", ".."); + UNIT_ASSERT_EQUAL(ps.IsAbsolute, true); + UNIT_ASSERT_EQUAL(ps.Drive, "C:"); + } + #endif + + Y_UNIT_TEST(WinMixed) { + PSUF(TPathSplit) + ps("some\\usual/path"); + #ifdef _win_ + PS_CHECK(ps, "some", "usual", "path"); + #else + PS_CHECK(ps, "some\\usual", "path"); + #endif + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + } + + Y_UNIT_TEST(WinParsePartMixed) { + PSUF(TPathSplit) + ps("some\\usual/path"); + ps.ParsePart("sub/sub\\path"); + #ifdef _win_ + PS_CHECK(ps, "some", "usual", "path", "sub", "sub", "path"); + #else + PS_CHECK(ps, "some\\usual", "path", "sub", "sub\\path"); + #endif + UNIT_ASSERT_EQUAL(ps.IsAbsolute, false); + } + + Y_UNIT_TEST(BeginWithSelf) { + PSUF(TPathSplit) + ps("./some/usual/path"); + PS_CHECK(ps, "some", "usual", "path"); + #ifdef _win_ + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "some\\usual\\path"); + #else + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "some/usual/path"); + #endif + } + + Y_UNIT_TEST(BeginWithParent) { + PSUF(TPathSplit) + ps("../some/usual/path"); + PS_CHECK(ps, "..", "some", "usual", "path"); + #ifdef _win_ + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "..\\some\\usual\\path"); + #else + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "../some/usual/path"); + #endif + } + + Y_UNIT_TEST(InOut) { + PSUF(TPathSplit) + ps("path/.."); + PS_CHECK(ps); + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), ""); + } + + Y_UNIT_TEST(OutIn) { + PSUF(TPathSplit) + ps("../path"); + PS_CHECK(ps, "..", "path"); + #ifdef _win_ + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "..\\path"); + #else + UNIT_ASSERT_STRINGS_EQUAL(ps.Reconstruct(), "../path"); + #endif + } +} + +Y_UNIT_TEST_SUITE(PSUF(PathSplitTraits)) { + Y_UNIT_TEST(IsPathSep) { + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsPathSep('/'), true); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsPathSep('\\'), TRUE_ONLY_WIN); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsPathSep(' '), false); + } + + Y_UNIT_TEST(IsAbsolutePath) { + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath(""), false); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("/"), true); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("some/usual/path"), false); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("/some/usual/path"), true); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("."), false); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath(".."), false); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("/."), true); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("/.."), true); + } + + Y_UNIT_TEST(WinIsAbsolutePath) { + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("somepath"), false); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("\\"), TRUE_ONLY_WIN); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("\\somepath"), TRUE_ONLY_WIN); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("\\."), TRUE_ONLY_WIN); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("\\.."), TRUE_ONLY_WIN); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("C"), false); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("C:"), TRUE_ONLY_WIN); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("C:somepath"), false); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("C:\\"), TRUE_ONLY_WIN); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("C:\\somepath"), TRUE_ONLY_WIN); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("C:/"), TRUE_ONLY_WIN); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("C:/somepath"), TRUE_ONLY_WIN); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("#:"), false); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("#:somepath"), false); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("#:\\somepath"), false); + UNIT_ASSERT_EQUAL(PSUF_LOCAL(TPathSplitTraits)::IsAbsolutePath("#:/somepath"), false); + } +} + + #undef TRUE_ONLY_WIN + +#endif diff --git a/util/folder/tempdir.cpp b/util/folder/tempdir.cpp new file mode 100644 index 00000000000..6fdf8f753c1 --- /dev/null +++ b/util/folder/tempdir.cpp @@ -0,0 +1,44 @@ +#include "tempdir.h" + +#include "dirut.h" + +#include +#include + +TTempDir::TTempDir() + : TTempDir(nullptr, TCreationToken{}) +{ +} + +TTempDir::TTempDir(const char* prefix, TCreationToken) + : TempDir() + , Remove(true) +{ + char tempDir[MAX_PATH]; + if (MakeTempDir(tempDir, prefix) != 0) { + ythrow TSystemError() << "Can't create temporary directory"; + } + TempDir = tempDir; +} + +TTempDir::TTempDir(const TString& tempDir) + : TempDir(tempDir) + , Remove(true) +{ + NFs::Remove(TempDir); + MakeDirIfNotExist(TempDir.c_str()); +} + +TTempDir TTempDir::NewTempDir(const TString& root) { + return {root.c_str(), TCreationToken{}}; +} + +void TTempDir::DoNotRemove() { + Remove = false; +} + +TTempDir::~TTempDir() { + if (Remove) { + RemoveDirWithContents(TempDir); + } +} diff --git a/util/folder/tempdir.h b/util/folder/tempdir.h new file mode 100644 index 00000000000..ff458f83b9b --- /dev/null +++ b/util/folder/tempdir.h @@ -0,0 +1,43 @@ +#pragma once + +#include "fwd.h" +#include "path.h" +#include + +class TTempDir { +public: + /// Create new directory in system tmp folder. + TTempDir(); + + /// Create new directory with this fixed name. If it already exists, clear it. + TTempDir(const TString& tempDir); + + ~TTempDir(); + + /// Create new directory in given folder. + static TTempDir NewTempDir(const TString& root); + + const TString& operator()() const { + return Name(); + } + + const TString& Name() const { + return TempDir.GetPath(); + } + + const TFsPath& Path() const { + return TempDir; + } + + void DoNotRemove(); + +private: + struct TCreationToken {}; + + // Prevent people from confusing this ctor with the public one + // by requiring additional fake argument. + TTempDir(const char* prefix, TCreationToken); + + TFsPath TempDir; + bool Remove; +}; diff --git a/util/folder/ut/ya.make b/util/folder/ut/ya.make new file mode 100644 index 00000000000..c5828a634cb --- /dev/null +++ b/util/folder/ut/ya.make @@ -0,0 +1,18 @@ +UNITTEST_FOR(util) + +SRCS( + folder/dirut_ut.cpp + folder/filelist_ut.cpp + folder/fts_ut.cpp + folder/iterator_ut.cpp + folder/path_ut.cpp + folder/pathsplit_ut.cpp +) + +PEERDIR( + library/cpp/threading/future +) + +INCLUDE(${ARCADIA_ROOT}/util/tests/ya_util_tests.inc) + +END() diff --git a/util/folder/ut_cython/test_folder.py b/util/folder/ut_cython/test_folder.py new file mode 100644 index 00000000000..ca176ab2a5d --- /dev/null +++ b/util/folder/ut_cython/test_folder.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +from __future__ import print_function, absolute_import, division + +from util.folder.path_ut import TestPath + +# Test discovery does not work in cython modules. +# Reexporting test classes here to satisfy pylint and pytest. + +__all__ = [ + 'TestPath', +] diff --git a/util/folder/ut_cython/ya.make b/util/folder/ut_cython/ya.make new file mode 100644 index 00000000000..02516df4813 --- /dev/null +++ b/util/folder/ut_cython/ya.make @@ -0,0 +1,14 @@ +PY23_TEST() + +SRCDIR(util/folder) + +PY_SRCS( + NAMESPACE util.folder + path_ut.pyx +) + +TEST_SRCS( + test_folder.py +) + +END() diff --git a/util/folder/ya.make b/util/folder/ya.make new file mode 100644 index 00000000000..f1db7b12aea --- /dev/null +++ b/util/folder/ya.make @@ -0,0 +1,9 @@ +RECURSE_FOR_TESTS( + ut +) + +IF (NOT OS_IOS AND NOT OS_ANDROID AND NOT OS_EMSCRIPTEN AND NOT USE_SYSTEM_PYTHON) + RECURSE( + ut_cython + ) +ENDIF() diff --git a/util/generic/adaptor.cpp b/util/generic/adaptor.cpp new file mode 100644 index 00000000000..1909857ba5f --- /dev/null +++ b/util/generic/adaptor.cpp @@ -0,0 +1 @@ +#include "adaptor.h" diff --git a/util/generic/adaptor.h b/util/generic/adaptor.h new file mode 100644 index 00000000000..b88a65fc81e --- /dev/null +++ b/util/generic/adaptor.h @@ -0,0 +1,140 @@ +#pragma once + +#include "store_policy.h" +#include "typetraits.h" + +namespace NPrivate { + template + class TReverseRangeStorage { + public: + TReverseRangeStorage(Range&& range) + : Base_(std::forward(range)) + { + } + + decltype(auto) Base() const { + return *Base_.Ptr(); + } + + decltype(auto) Base() { + return *Base_.Ptr(); + } + + private: + TAutoEmbedOrPtrPolicy Base_; + }; + + template + constexpr bool HasReverseIterators(i32, decltype(std::declval().rbegin())*) { + return true; + } + + template + constexpr bool HasReverseIterators(char, std::nullptr_t*) { + return false; + } + + template ((i32)0, nullptr)> + class TReverseRangeBase: public TReverseRangeStorage { + using TBase = TReverseRangeStorage; + + public: + using TBase::Base; + using TBase::TBase; + + auto begin() const { + return Base().rbegin(); + } + + auto end() const { + return Base().rend(); + } + + auto begin() { + return Base().rbegin(); + } + + auto end() { + return Base().rend(); + } + }; + + template + class TReverseRangeBase: public TReverseRangeStorage { + using TBase = TReverseRangeStorage; + + public: + using TBase::Base; + using TBase::TBase; + + auto begin() const { + using std::end; + return std::make_reverse_iterator(end(Base())); + } + + auto end() const { + using std::begin; + return std::make_reverse_iterator(begin(Base())); + } + + auto begin() { + using std::end; + return std::make_reverse_iterator(end(Base())); + } + + auto end() { + using std::begin; + return std::make_reverse_iterator(begin(Base())); + } + }; + + template + class TReverseRange: public TReverseRangeBase { + using TBase = TReverseRangeBase; + + public: + using TBase::Base; + using TBase::TBase; + + TReverseRange(TReverseRange&&) = default; + TReverseRange(const TReverseRange&) = default; + + auto rbegin() const { + using std::begin; + return begin(Base()); + } + + auto rend() const { + using std::end; + return end(Base()); + } + + auto rbegin() { + using std::begin; + return begin(Base()); + } + + auto rend() { + using std::end; + return end(Base()); + } + }; +} + +/** + * Provides a reverse view into the provided container. + * + * Example usage: + * @code + * for(auto&& value: Reversed(container)) { + * // use value here. + * } + * @endcode + * + * @param cont Container to provide a view into. Must be an lvalue. + * @returns A reverse view into the provided container. + */ +template +constexpr ::NPrivate::TReverseRange Reversed(Range&& range) { + return ::NPrivate::TReverseRange(std::forward(range)); +} diff --git a/util/generic/adaptor_ut.cpp b/util/generic/adaptor_ut.cpp new file mode 100644 index 00000000000..721f849f934 --- /dev/null +++ b/util/generic/adaptor_ut.cpp @@ -0,0 +1,124 @@ +#include "adaptor.h" +#include "yexception.h" + +#include + +struct TOnCopy: yexception { +}; + +struct TOnMove: yexception { +}; + +struct TState { + explicit TState() { + } + + TState(const TState&) { + ythrow TOnCopy(); + } + + TState(TState&&) { + ythrow TOnMove(); + } + + void operator=(const TState&) { + ythrow TOnCopy(); + } + + void rbegin() const { + } + + void rend() const { + } +}; + +Y_UNIT_TEST_SUITE(TReverseAdaptor) { + Y_UNIT_TEST(ReadTest) { + TVector cont = {1, 2, 3}; + TVector etalon = {3, 2, 1}; + size_t idx = 0; + for (const auto& x : Reversed(cont)) { + UNIT_ASSERT_VALUES_EQUAL(etalon[idx++], x); + } + idx = 0; + for (const auto& x : Reversed(std::move(cont))) { + UNIT_ASSERT_VALUES_EQUAL(etalon[idx++], x); + } + } + + Y_UNIT_TEST(WriteTest) { + TVector cont = {1, 2, 3}; + TVector etalon = {3, 6, 9}; + size_t idx = 0; + for (auto& x : Reversed(cont)) { + x *= x + idx++; + } + idx = 0; + for (auto& x : cont) { + UNIT_ASSERT_VALUES_EQUAL(etalon[idx++], x); + } + } + + Y_UNIT_TEST(InnerTypeTest) { + using TStub = TVector; + TStub stub; + const TStub cstub; + + using namespace NPrivate; + UNIT_ASSERT_TYPES_EQUAL(decltype(Reversed(stub)), TReverseRange); + UNIT_ASSERT_TYPES_EQUAL(decltype(Reversed(cstub)), TReverseRange); + } + + Y_UNIT_TEST(CopyMoveTest) { + TState lvalue; + const TState clvalue; + UNIT_ASSERT_NO_EXCEPTION(Reversed(lvalue)); + UNIT_ASSERT_NO_EXCEPTION(Reversed(clvalue)); + } + + Y_UNIT_TEST(ReverseX2Test) { + TVector cont = {1, 2, 3}; + size_t idx = 0; + for (const auto& x : Reversed(Reversed(cont))) { + UNIT_ASSERT_VALUES_EQUAL(cont[idx++], x); + } + } + + Y_UNIT_TEST(ReverseX3Test) { + TVector cont = {1, 2, 3}; + TVector etalon = {3, 2, 1}; + size_t idx = 0; + for (const auto& x : Reversed(Reversed(Reversed(cont)))) { + UNIT_ASSERT_VALUES_EQUAL(etalon[idx++], x); + } + } + + Y_UNIT_TEST(ReverseTemporaryTest) { + TVector etalon = {3, 2, 1}; + TVector etalon2 = {1, 2, 3}; + size_t idx = 0; + for (const auto& x : Reversed(TVector{1, 2, 3})) { + UNIT_ASSERT_VALUES_EQUAL(etalon[idx++], x); + } + idx = 0; + for (const auto& x : Reversed(Reversed(TVector{1, 2, 3}))) { + UNIT_ASSERT_VALUES_EQUAL(etalon2[idx++], x); + } + } + + Y_UNIT_TEST(ReverseInitializerListTest) { + // initializer_list has no rbegin and rend + auto cont = {1, 2, 3}; + TVector etalon = {3, 2, 1}; + TVector etalon2 = {1, 2, 3}; + + size_t idx = 0; + for (const auto& x : Reversed(cont)) { + UNIT_ASSERT_VALUES_EQUAL(etalon[idx++], x); + } + idx = 0; + for (const auto& x : Reversed(Reversed(cont))) { + UNIT_ASSERT_VALUES_EQUAL(etalon2[idx++], x); + } + } +} diff --git a/util/generic/algorithm.cpp b/util/generic/algorithm.cpp new file mode 100644 index 00000000000..d89586737e1 --- /dev/null +++ b/util/generic/algorithm.cpp @@ -0,0 +1 @@ +#include "algorithm.h" diff --git a/util/generic/algorithm.h b/util/generic/algorithm.h new file mode 100644 index 00000000000..70efc506092 --- /dev/null +++ b/util/generic/algorithm.h @@ -0,0 +1,800 @@ +#pragma once + +#include "is_in.h" +#include "utility.h" + +#include +#include + +#include +#include +#include +#include + +namespace NPrivate { + template + constexpr I ExtremeElementBy(I begin, I end, F&& func, P&& pred) { + if (begin == end) { + return end; + } + + auto bestValue = func(*begin); + auto bestPos = begin; + + for (auto i = ++begin; i != end; ++i) { + auto curValue = func(*i); + if (pred(curValue, bestValue)) { + bestValue = curValue; + bestPos = i; + } + } + + return bestPos; + } +} + +template +constexpr void Sort(T f, T l) { + std::sort(f, l); +} + +template +constexpr void Sort(T f, T l, C c) { + std::sort(f, l, c); +} + +template +constexpr void Sort(TContainer& container) { + Sort(container.begin(), container.end()); +} + +template +constexpr void Sort(TContainer& container, TCompare compare) { + Sort(container.begin(), container.end(), compare); +} + +template +constexpr void SortBy(TIterator begin, TIterator end, const TGetKey& getKey) { + Sort(begin, end, [&](auto&& left, auto&& right) { return getKey(left) < getKey(right); }); +} + +template +constexpr void SortBy(TContainer& container, const TGetKey& getKey) { + SortBy(container.begin(), container.end(), getKey); +} + +template +static inline void StableSort(T f, T l) { + std::stable_sort(f, l); +} + +template +static inline void StableSort(T f, T l, C c) { + std::stable_sort(f, l, c); +} + +template +static inline void StableSort(TContainer& container) { + StableSort(container.begin(), container.end()); +} + +template +static inline void StableSort(TContainer& container, TCompare compare) { + StableSort(container.begin(), container.end(), compare); +} + +template +static inline void StableSortBy(TIterator begin, TIterator end, const TGetKey& getKey) { + StableSort(begin, end, [&](auto&& left, auto&& right) { return getKey(left) < getKey(right); }); +} + +template +static inline void StableSortBy(TContainer& container, const TGetKey& getKey) { + StableSortBy(container.begin(), container.end(), getKey); +} + +template +constexpr void PartialSort(T f, T m, T l) { + std::partial_sort(f, m, l); +} + +template +constexpr void PartialSort(T f, T m, T l, C c) { + std::partial_sort(f, m, l, c); +} + +template +constexpr R PartialSortCopy(T f, T l, R of, R ol) { + return std::partial_sort_copy(f, l, of, ol); +} + +template +constexpr R PartialSortCopy(T f, T l, R of, R ol, C c) { + return std::partial_sort_copy(f, l, of, ol, c); +} + +template +constexpr I Find(I f, I l, const T& v) { + return std::find(f, l, v); +} + +template +constexpr auto Find(C&& c, const T& v) { + using std::begin; + using std::end; + + return std::find(begin(c), end(c), v); +} + +// FindPtr - return NULL if not found. Works for arrays, containers, iterators +template +constexpr auto FindPtr(I f, I l, const T& v) -> decltype(&*f) { + I found = Find(f, l, v); + return (found != l) ? &*found : nullptr; +} + +template +constexpr auto FindPtr(C&& c, const T& v) { + using std::begin; + using std::end; + return FindPtr(begin(c), end(c), v); +} + +template +constexpr I FindIf(I f, I l, P p) { + return std::find_if(f, l, p); +} + +template +constexpr auto FindIf(C&& c, P p) { + using std::begin; + using std::end; + + return FindIf(begin(c), end(c), p); +} + +template +constexpr bool AllOf(I f, I l, P pred) { + return std::all_of(f, l, pred); +} + +template +constexpr bool AllOf(const C& c, P pred) { + using std::begin; + using std::end; + return AllOf(begin(c), end(c), pred); +} + +template +constexpr bool AnyOf(I f, I l, P pred) { + return std::any_of(f, l, pred); +} + +template +constexpr bool AnyOf(const C& c, P pred) { + using std::begin; + using std::end; + return AnyOf(begin(c), end(c), pred); +} + +// FindIfPtr - return NULL if not found. Works for arrays, containers, iterators +template +constexpr auto FindIfPtr(I f, I l, P pred) -> decltype(&*f) { + I found = FindIf(f, l, pred); + return (found != l) ? &*found : nullptr; +} + +template +constexpr auto FindIfPtr(C&& c, P pred) { + using std::begin; + using std::end; + return FindIfPtr(begin(c), end(c), pred); +} + +template +constexpr size_t FindIndex(C&& c, const T& x) { + using std::begin; + using std::end; + auto it = Find(begin(c), end(c), x); + return it == end(c) ? NPOS : (it - begin(c)); +} + +template +constexpr size_t FindIndexIf(C&& c, P p) { + using std::begin; + using std::end; + auto it = FindIf(begin(c), end(c), p); + return it == end(c) ? NPOS : (it - begin(c)); +} + +//EqualToOneOf(x, "apple", "orange") means (x == "apple" || x == "orange") +template +constexpr bool EqualToOneOf(const T&) { + return false; +} + +template +constexpr bool EqualToOneOf(const T& x, const U& y, const Other&... other) { + return x == y || EqualToOneOf(x, other...); +} + +template +constexpr size_t CountOf(const T&) { + return 0; +} + +template +constexpr size_t CountOf(const T& x, const U& y, const Other&... other) { + return static_cast(x == y) + CountOf(x, other...); +} + +template +constexpr void PushHeap(I f, I l) { + std::push_heap(f, l); +} + +template +constexpr void PushHeap(I f, I l, C c) { + std::push_heap(f, l, c); +} + +template +constexpr void PopHeap(I f, I l) { + std::pop_heap(f, l); +} + +template +constexpr void PopHeap(I f, I l, C c) { + std::pop_heap(f, l, c); +} + +template +constexpr void MakeHeap(I f, I l) { + std::make_heap(f, l); +} + +template +constexpr void MakeHeap(I f, I l, C c) { + std::make_heap(f, l, c); +} + +template +constexpr void SortHeap(I f, I l) { + std::sort_heap(f, l); +} + +template +constexpr void SortHeap(I f, I l, C c) { + std::sort_heap(f, l, c); +} + +template +constexpr I LowerBound(I f, I l, const T& v) { + return std::lower_bound(f, l, v); +} + +template +constexpr I LowerBound(I f, I l, const T& v, C c) { + return std::lower_bound(f, l, v, c); +} + +template +constexpr I LowerBoundBy(I f, I l, const T& v, const TGetKey& getKey) { + return std::lower_bound(f, l, v, [&](auto&& left, auto&& right) { return getKey(left) < right; }); +} + +template +constexpr I UpperBound(I f, I l, const T& v) { + return std::upper_bound(f, l, v); +} + +template +constexpr I UpperBound(I f, I l, const T& v, C c) { + return std::upper_bound(f, l, v, c); +} + +template +constexpr I UpperBoundBy(I f, I l, const T& v, const TGetKey& getKey) { + return std::upper_bound(f, l, v, [&](auto&& left, auto&& right) { return left < getKey(right); }); +} + +template +constexpr T Unique(T f, T l) { + return std::unique(f, l); +} + +template +constexpr T Unique(T f, T l, P p) { + return std::unique(f, l, p); +} + +template +constexpr T UniqueBy(T f, T l, const TGetKey& getKey) { + return Unique(f, l, [&](auto&& left, auto&& right) { return getKey(left) == getKey(right); }); +} + +template +void SortUnique(C& c) { + Sort(c.begin(), c.end()); + c.erase(Unique(c.begin(), c.end()), c.end()); +} + +template +void SortUnique(C& c, Cmp cmp) { + Sort(c.begin(), c.end(), cmp); + c.erase(Unique(c.begin(), c.end()), c.end()); +} + +template +void SortUniqueBy(C& c, const TGetKey& getKey) { + SortBy(c, getKey); + c.erase(UniqueBy(c.begin(), c.end(), getKey), c.end()); +} + +template +void StableSortUniqueBy(C& c, const TGetKey& getKey) { + StableSortBy(c, getKey); + c.erase(UniqueBy(c.begin(), c.end(), getKey), c.end()); +} + +template +void Erase(C& c, const TValue& value) { + c.erase(std::remove(c.begin(), c.end(), value), c.end()); +} + +template +void EraseIf(C& c, P p) { + c.erase(std::remove_if(c.begin(), c.end(), p), c.end()); +} + +template +void EraseNodesIf(C& c, P p) { + for (auto iter = c.begin(), last = c.end(); iter != last;) { + if (p(*iter)) { + c.erase(iter++); + } else { + ++iter; + } + } +} + +template +constexpr bool Equal(T1 f1, T1 l1, T2 f2) { + return std::equal(f1, l1, f2); +} + +template +constexpr bool Equal(T1 f1, T1 l1, T2 f2, P p) { + return std::equal(f1, l1, f2, p); +} + +template +constexpr TO Copy(TI f, TI l, TO t) { + return std::copy(f, l, t); +} + +template +constexpr TO UniqueCopy(TI f, TI l, TO t) { + return std::unique_copy(f, l, t); +} + +template +constexpr TO UniqueCopy(TI f, TI l, TO t, TP p) { + return std::unique_copy(f, l, t, p); +} + +template +constexpr TO RemoveCopyIf(TI f, TI l, TO t, TP p) { + return std::remove_copy_if(f, l, t, p); +} + +template +constexpr TO ReverseCopy(TI f, TI l, TO t) { + return std::reverse_copy(f, l, t); +} + +template +constexpr TO SetUnion(TI1 f1, TI1 l1, TI2 f2, TI2 l2, TO p) { + return std::set_union(f1, l1, f2, l2, p); +} + +template +constexpr TO SetUnion(TI1 f1, TI1 l1, TI2 f2, TI2 l2, TO p, TC c) { + return std::set_union(f1, l1, f2, l2, p, c); +} + +template +constexpr TO SetDifference(TI1 f1, TI1 l1, TI2 f2, TI2 l2, TO p) { + return std::set_difference(f1, l1, f2, l2, p); +} + +template +constexpr TO SetDifference(TI1 f1, TI1 l1, TI2 f2, TI2 l2, TO p, TC c) { + return std::set_difference(f1, l1, f2, l2, p, c); +} + +template +constexpr TO SetSymmetricDifference(TI1 f1, TI1 l1, TI2 f2, TI2 l2, TO p) { + return std::set_symmetric_difference(f1, l1, f2, l2, p); +} + +template +constexpr TO SetSymmetricDifference(TI1 f1, TI1 l1, TI2 f2, TI2 l2, TO p, TC c) { + return std::set_symmetric_difference(f1, l1, f2, l2, p, c); +} + +template +constexpr TO SetIntersection(TI1 f1, TI1 l1, TI2 f2, TI2 l2, TO p) { + return std::set_intersection(f1, l1, f2, l2, p); +} + +template +constexpr TO SetIntersection(TI1 f1, TI1 l1, TI2 f2, TI2 l2, TO p, TC c) { + return std::set_intersection(f1, l1, f2, l2, p, c); +} + +template +constexpr void Fill(I f, I l, const T& v) { + std::fill(f, l, v); +} + +template +constexpr I FillN(I f, S n, const T& v) { + return std::fill_n(f, n, v); +} + +template +constexpr void Reverse(T f, T l) { + std::reverse(f, l); +} + +template +constexpr void Rotate(T f, T m, T l) { + std::rotate(f, m, l); +} + +template +constexpr Val Accumulate(It begin, It end, Val val) { + // std::move since C++20 + return std::accumulate(begin, end, std::move(val)); +} + +template +constexpr Val Accumulate(It begin, It end, Val val, BinOp binOp) { + // std::move since C++20 + return std::accumulate(begin, end, std::move(val), binOp); +} + +template +constexpr Val Accumulate(const C& c, Val val) { + // std::move since C++20 + return Accumulate(std::begin(c), std::end(c), std::move(val)); +} + +template +constexpr Val Accumulate(const C& c, Val val, BinOp binOp) { + // std::move since C++20 + return Accumulate(std::begin(c), std::end(c), std::move(val), binOp); +} + +template +constexpr Val InnerProduct(It1 begin1, It1 end1, It2 begin2, Val val) { + return std::inner_product(begin1, end1, begin2, val); +} + +template +constexpr Val InnerProduct(It1 begin1, It1 end1, It2 begin2, Val val, BinOp1 binOp1, BinOp2 binOp2) { + return std::inner_product(begin1, end1, begin2, val, binOp1, binOp2); +} + +template +constexpr typename TVectorType::value_type InnerProduct(const TVectorType& lhs, const TVectorType& rhs, typename TVectorType::value_type val = typename TVectorType::value_type()) { + return std::inner_product(lhs.begin(), lhs.end(), rhs.begin(), val); +} + +template +constexpr typename TVectorType::value_type InnerProduct(const TVectorType& lhs, const TVectorType& rhs, typename TVectorType::value_type val, BinOp1 binOp1, BinOp2 binOp2) { + return std::inner_product(lhs.begin(), lhs.end(), rhs.begin(), val, binOp1, binOp2); +} + +template +constexpr T MinElement(T begin, T end) { + return std::min_element(begin, end); +} + +template +constexpr T MinElement(T begin, T end, C comp) { + return std::min_element(begin, end, comp); +} + +template +constexpr T MaxElement(T begin, T end) { + return std::max_element(begin, end); +} + +template +constexpr T MaxElement(T begin, T end, C comp) { + return std::max_element(begin, end, comp); +} + +template +constexpr I MaxElementBy(I begin, I end, F&& func) { + using TValue = decltype(func(*begin)); + return ::NPrivate::ExtremeElementBy(begin, end, std::forward(func), TGreater()); +} + +template +constexpr auto MaxElementBy(C& c, F&& func) { + return MaxElementBy(std::begin(c), std::end(c), std::forward(func)); +} + +template +constexpr auto MaxElementBy(const C& c, F&& func) { + return MaxElementBy(std::begin(c), std::end(c), std::forward(func)); +} + +template +constexpr I MinElementBy(I begin, I end, F&& func) { + using TValue = decltype(func(*begin)); + return ::NPrivate::ExtremeElementBy(begin, end, std::forward(func), TLess()); +} + +template +constexpr auto MinElementBy(C& c, F&& func) { + return MinElementBy(std::begin(c), std::end(c), std::forward(func)); +} + +template +constexpr auto MinElementBy(const C& c, F&& func) { + return MinElementBy(std::begin(c), std::end(c), std::forward(func)); +} + +template +void ApplyToMany(TOp op, TArgs&&... args) { + int dummy[] = {((void)op(std::forward(args)), 0)...}; + Y_UNUSED(dummy); +} + +template +constexpr void ForEach(TI f, TI l, TOp op) { + std::for_each(f, l, op); +} + +namespace NPrivate { + template + constexpr bool AllOfImpl(T&& t, TOp&& op, std::index_sequence) { +#if _LIBCPP_STD_VER >= 17 + return (true && ... && op(std::get(std::forward(t)))); +#else + bool result = true; + auto wrapper = [&result, &op](auto&& x) { result = result && op(std::forward(x)); }; + int dummy[] = {(wrapper(std::get(std::forward(t))), 0)...}; + Y_UNUSED(dummy); + return result; +#endif + } + + template + constexpr bool AnyOfImpl(T&& t, TOp&& op, std::index_sequence) { +#if _LIBCPP_STD_VER >= 17 + return (false || ... || op(std::get(std::forward(t)))); +#else + bool result = false; + auto wrapper = [&result, &op](auto&& x) { result = result || op(std::forward(x)); }; + int dummy[] = {(wrapper(std::get(std::forward(t))), 0)...}; + Y_UNUSED(dummy); + return result; +#endif + } + + template + constexpr void ForEachImpl(T&& t, TOp&& op, std::index_sequence) { +#if _LIBCPP_STD_VER >= 17 + (..., op(std::get(std::forward(t)))); +#else + ::ApplyToMany(std::forward(op), std::get(std::forward(t))...); +#endif + } +} + +// check that TOp return true for all of element from tuple T +template +constexpr ::TEnableIfTuple AllOf(T&& t, TOp&& op) { + return ::NPrivate::AllOfImpl( + std::forward(t), + std::forward(op), + std::make_index_sequence>::value>{}); +} + +// check that TOp return true for at least one element from tuple T +template +constexpr ::TEnableIfTuple AnyOf(T&& t, TOp&& op) { + return ::NPrivate::AnyOfImpl( + std::forward(t), + std::forward(op), + std::make_index_sequence>::value>{}); +} + +template +constexpr ::TEnableIfTuple ForEach(T&& t, TOp&& op) { + ::NPrivate::ForEachImpl( + std::forward(t), + std::forward(op), + std::make_index_sequence>::value>{}); +} + +template +constexpr void Transform(T1 b, T1 e, T2 o, O f) { + std::transform(b, e, o, f); +} + +template +constexpr void Transform(T1 b1, T1 e1, T2 b2, T3 o, O f) { + std::transform(b1, e1, b2, o, f); +} + +template +constexpr typename std::iterator_traits::difference_type Count(T first, T last, const V& value) { + return std::count(first, last, value); +} + +template +constexpr auto Count(const TContainer& container, const TValue& value) { + return Count(std::cbegin(container), std::cend(container), value); +} + +template +constexpr auto CountIf(It first, It last, P p) { + return std::count_if(first, last, p); +} + +template +constexpr auto CountIf(const C& c, P pred) { + using std::begin; + using std::end; + return CountIf(begin(c), end(c), pred); +} + +template +constexpr std::pair Mismatch(I1 b1, I1 e1, I2 b2) { + return std::mismatch(b1, e1, b2); +} + +template +constexpr std::pair Mismatch(I1 b1, I1 e1, I2 b2, P p) { + return std::mismatch(b1, e1, b2, p); +} + +template +constexpr void NthElement(RandomIterator begin, RandomIterator nth, RandomIterator end) { + std::nth_element(begin, nth, end); +} + +template +constexpr void NthElement(RandomIterator begin, RandomIterator nth, RandomIterator end, Compare compare) { + std::nth_element(begin, nth, end, compare); +} + +// no standard implementation until C++14 +template +constexpr std::pair Mismatch(I1 b1, I1 e1, I2 b2, I2 e2) { + while (b1 != e1 && b2 != e2 && *b1 == *b2) { + ++b1; + ++b2; + } + return std::make_pair(b1, b2); +} + +template +constexpr std::pair Mismatch(I1 b1, I1 e1, I2 b2, I2 e2, P p) { + while (b1 != e1 && b2 != e2 && p(*b1, *b2)) { + ++b1; + ++b2; + } + return std::make_pair(b1, b2); +} + +template +constexpr bool BinarySearch(It begin, It end, const Val& val) { + return std::binary_search(begin, end, val); +} + +template +constexpr bool BinarySearch(It begin, It end, const Val& val, Comp comp) { + return std::binary_search(begin, end, val, comp); +} + +template +constexpr std::pair EqualRange(It begin, It end, const Val& val) { + return std::equal_range(begin, end, val); +} + +template +constexpr std::pair EqualRange(It begin, It end, const Val& val, Comp comp) { + return std::equal_range(begin, end, val, comp); +} + +template +constexpr auto AdjacentFind(TContainer&& c) { + using std::begin; + using std::end; + return std::adjacent_find(begin(c), end(c)); +} + +template +constexpr auto AdjacentFind(TContainer&& c, Compare comp) { + using std::begin; + using std::end; + return std::adjacent_find(begin(c), end(c), comp); +} + +namespace NPrivate { + template + constexpr TForwardIterator AdjacentFindBy(TForwardIterator begin, TForwardIterator end, const TGetKey& getKey) { + return std::adjacent_find(begin, end, [&](auto&& left, auto&& right) { return getKey(left) == getKey(right); }); + } +} + +template +constexpr auto AdjacentFindBy(TContainer&& c, const TGetKey& getKey) { + using std::begin; + using std::end; + return ::NPrivate::AdjacentFindBy(begin(c), end(c), getKey); +} + +template +constexpr bool IsSorted(ForwardIt begin, ForwardIt end) { + return std::is_sorted(begin, end); +} + +template +constexpr bool IsSorted(ForwardIt begin, ForwardIt end, Compare comp) { + return std::is_sorted(begin, end, comp); +} + +template +constexpr bool IsSortedBy(TIterator begin, TIterator end, const TGetKey& getKey) { + return IsSorted(begin, end, [&](auto&& left, auto&& right) { return getKey(left) < getKey(right); }); +} + +template +constexpr bool IsSortedBy(const TContainer& c, const TGetKey& getKey) { + using std::begin; + using std::end; + return IsSortedBy(begin(c), end(c), getKey); +} + +template +constexpr void Iota(It begin, It end, Val val) { + std::iota(begin, end, val); +} + +template +constexpr TO CopyN(TI from, S s, TO to) { + return std::copy_n(from, s, to); +} + +template +constexpr TO CopyIf(TI begin, TI end, TO to, P pred) { + return std::copy_if(begin, end, to, pred); +} + +template +constexpr std::pair MinMax(const T& first, const T& second) { + return std::minmax(first, second); +} + +template +constexpr std::pair MinMaxElement(It first, It last) { + return std::minmax_element(first, last); +} + +template +constexpr void Generate(TIterator first, TIterator last, TGenerator generator) { + std::generate(first, last, generator); +} + +template +constexpr void GenerateN(TIterator first, TSize count, TGenerator generator) { + std::generate_n(first, count, generator); +} diff --git a/util/generic/algorithm_ut.cpp b/util/generic/algorithm_ut.cpp new file mode 100644 index 00000000000..09c5824cd9b --- /dev/null +++ b/util/generic/algorithm_ut.cpp @@ -0,0 +1,906 @@ +#include + +#include "algorithm.h" +#include "hash.h" +#include "hash_multi_map.h" +#include "strbuf.h" +#include "string.h" + +static auto isOne = [](char c) { return c == '1'; }; + +Y_UNIT_TEST_SUITE(TAlgorithm) { + Y_UNIT_TEST(AnyTest) { + UNIT_ASSERT(0 == AnyOf(TStringBuf("00"), isOne)); + UNIT_ASSERT(1 == AnyOf(TStringBuf("01"), isOne)); + UNIT_ASSERT(1 == AnyOf(TStringBuf("10"), isOne)); + UNIT_ASSERT(1 == AnyOf(TStringBuf("11"), isOne)); + UNIT_ASSERT(0 == AnyOf(TStringBuf(), isOne)); + + const char array00[]{'0', '0'}; + UNIT_ASSERT(0 == AnyOf(array00, isOne)); + const char array01[]{'0', '1'}; + UNIT_ASSERT(1 == AnyOf(array01, isOne)); + } + + Y_UNIT_TEST(AllOfTest) { + UNIT_ASSERT(0 == AllOf(TStringBuf("00"), isOne)); + UNIT_ASSERT(0 == AllOf(TStringBuf("01"), isOne)); + UNIT_ASSERT(0 == AllOf(TStringBuf("10"), isOne)); + UNIT_ASSERT(1 == AllOf(TStringBuf("11"), isOne)); + UNIT_ASSERT(1 == AllOf(TStringBuf(), isOne)); + + const char array01[]{'0', '1'}; + UNIT_ASSERT(0 == AllOf(array01, isOne)); + const char array11[]{'1', '1'}; + UNIT_ASSERT(1 == AllOf(array11, isOne)); + } + + Y_UNIT_TEST(CountIfTest) { + UNIT_ASSERT(3 == CountIf(TStringBuf("____1________1____1_______"), isOne)); + UNIT_ASSERT(5 == CountIf(TStringBuf("1____1________1____1_______1"), isOne)); + UNIT_ASSERT(0 == CountIf(TStringBuf("___________"), isOne)); + UNIT_ASSERT(0 == CountIf(TStringBuf(), isOne)); + UNIT_ASSERT(1 == CountIf(TStringBuf("1"), isOne)); + + const char array[] = "____1________1____1_______"; + UNIT_ASSERT(3 == CountIf(array, isOne)); + } + + Y_UNIT_TEST(CountTest) { + UNIT_ASSERT(3 == Count("____1________1____1_______", '1')); + UNIT_ASSERT(3 == Count(TStringBuf("____1________1____1_______"), '1')); + UNIT_ASSERT(5 == Count(TStringBuf("1____1________1____1_______1"), '1')); + UNIT_ASSERT(0 == Count(TStringBuf("___________"), '1')); + UNIT_ASSERT(0 == Count(TStringBuf(), '1')); + UNIT_ASSERT(1 == Count(TStringBuf("1"), '1')); + + const char array[] = "____1________1____1_______"; + UNIT_ASSERT(3 == Count(array, '1')); + } + + struct TStrokaNoCopy: TString { + public: + TStrokaNoCopy(const char* p) + : TString(p) + { + } + + private: + TStrokaNoCopy(const TStrokaNoCopy&); + void operator=(const TStrokaNoCopy&); + }; + + Y_UNIT_TEST(CountOfTest) { + UNIT_ASSERT_VALUES_EQUAL(CountOf(1, 2), 0); + UNIT_ASSERT_VALUES_EQUAL(CountOf(1, 1), 1); + UNIT_ASSERT_VALUES_EQUAL(CountOf(2, 4, 5), 0); + UNIT_ASSERT_VALUES_EQUAL(CountOf(2, 4, 2), 1); + UNIT_ASSERT_VALUES_EQUAL(CountOf(3, 3, 3), 2); + + // Checking comparison of different types. + UNIT_ASSERT_VALUES_EQUAL(CountOf(0x61, 'x', 'y', 'z'), 0); + UNIT_ASSERT_VALUES_EQUAL(CountOf(0x61, 'a', 'b', 'c', 0x61), 2); + UNIT_ASSERT_VALUES_EQUAL(CountOf(0x61, 'a', 'b', 'c', 0x61ll), 2); + + // TString and const char * + UNIT_ASSERT_VALUES_EQUAL(CountOf(TString("xyz"), "123", "poi"), 0); + UNIT_ASSERT_VALUES_EQUAL(CountOf(TString("xyz"), "123", "poi", "xyz"), 1); + + // TString and TStringBuf + UNIT_ASSERT_VALUES_EQUAL(CountOf(TString("xyz"), TStringBuf("123"), TStringBuf("poi")), 0); + UNIT_ASSERT_VALUES_EQUAL(CountOf(TString("xyz"), TStringBuf("123"), TStringBuf("poi"), + TStringBuf("xyz")), + 1); + + // TStringBuf and const char * + UNIT_ASSERT_VALUES_EQUAL(CountOf(TStringBuf("xyz"), "123", "poi"), 0); + UNIT_ASSERT_VALUES_EQUAL(CountOf(TStringBuf("xyz"), "123", "poi", "xyz"), 1); + + // TStringBuf and TString + UNIT_ASSERT_VALUES_EQUAL(CountOf(TStringBuf("xyz"), TString("123"), TString("poi")), 0); + UNIT_ASSERT_VALUES_EQUAL(CountOf(TStringBuf("xyz"), TString("123"), TString("poi"), + TString("xyz")), + 1); + } + + Y_UNIT_TEST(EqualToOneOfTest) { + UNIT_ASSERT(1 == EqualToOneOf(1, 1, 2)); + UNIT_ASSERT(1 == EqualToOneOf(2, 1, 2)); + UNIT_ASSERT(0 == EqualToOneOf(3, 1, 2)); + UNIT_ASSERT(1 == EqualToOneOf(1, 1)); + UNIT_ASSERT(0 == EqualToOneOf(1, 2)); + UNIT_ASSERT(0 == EqualToOneOf(3)); + + //test, that EqualToOneOf can compare different types, and don't copy objects: + TStrokaNoCopy x("x"); + TStrokaNoCopy y("y"); + TStrokaNoCopy z("z"); + const char* px = "x"; + const char* py = "y"; + const char* pz = "z"; + + UNIT_ASSERT(1 == EqualToOneOf(x, px, py)); + UNIT_ASSERT(1 == EqualToOneOf(y, px, py)); + UNIT_ASSERT(1 == EqualToOneOf(y, px, y)); + UNIT_ASSERT(1 == EqualToOneOf(y, x, py)); + UNIT_ASSERT(0 == EqualToOneOf(z, px, py)); + UNIT_ASSERT(1 == EqualToOneOf(px, x, y)); + UNIT_ASSERT(1 == EqualToOneOf(py, x, y)); + UNIT_ASSERT(0 == EqualToOneOf(pz, x, y)); + } + + template + void TestFindPtrFoundValue(int j, TTestConstPtr root) { + if (j == 3) { + UNIT_ASSERT(root && *root == 3); + } else if (j == 4) { + UNIT_ASSERT(root == nullptr); + } else { + ythrow yexception() << "invalid param " << j; + } + } + + template + void TestFindIfPtrFoundValue(int j, TTestConstPtr root) { + if (j == 3) { + UNIT_ASSERT(root == nullptr); + } else if (j == 4) { + UNIT_ASSERT(root && *root == 2); + } else { + ythrow yexception() << "invalid param " << j; + } + } + + struct TVectorNoCopy: std::vector { + public: + TVectorNoCopy() = default; + + private: + TVectorNoCopy(const TVectorNoCopy&); + void operator=(const TVectorNoCopy&); + }; + + Y_UNIT_TEST(FindPtrTest) { + TVectorNoCopy v; + v.push_back(1); + v.push_back(2); + v.push_back(3); + + int array[3] = {1, 2, 3}; + const int array_const[3] = {1, 2, 3}; + + //test (const, non-const) * (iterator, vector, array) * (found, not found) variants. + // value '3' is in container, value '4' is not + for (int j = 3; j <= 4; ++j) { + TestFindPtrFoundValue(j, FindPtr(v, j)); + TestFindPtrFoundValue(j, FindPtr(v.begin(), v.end(), j)); + const TVectorNoCopy& q = v; + TestFindPtrFoundValue(j, FindPtr(q, j)); + TestFindPtrFoundValue(j, FindPtr(q.begin(), q.end(), j)); + TestFindPtrFoundValue(j, FindPtr(array, j)); + TestFindPtrFoundValue(j, FindPtr(array_const, j)); + } + } + + Y_UNIT_TEST(FindIfPtrTest) { + TVectorNoCopy v; + v.push_back(1); + v.push_back(2); + v.push_back(3); + + int array[3] = {1, 2, 3}; + const int array_const[3] = {1, 2, 3}; + + //test (const, non-const) * (iterator, vector, array) * (found, not found) variants. + // search, that 2*2 == 4, but there is no value 'x' in array that (x*x == 3) + for (int j = 3; j <= 4; ++j) { + TestFindIfPtrFoundValue(j, FindIfPtr(v, [j](int i) { return i * i == j; })); + TestFindIfPtrFoundValue(j, FindIfPtr(v.begin(), v.end(), [j](int i) { return i * i == j; })); + const TVectorNoCopy& q = v; + TestFindIfPtrFoundValue(j, FindIfPtr(q, [j](int i) { return i * i == j; })); + + TestFindIfPtrFoundValue(j, FindIfPtr(q.begin(), q.end(), [j](int i) { return i * i == j; })); + TestFindIfPtrFoundValue(j, FindIfPtr(array, [j](int i) { return i * i == j; })); + TestFindIfPtrFoundValue(j, FindIfPtr(array_const, [j](int i) { return i * i == j; })); + } + } + + Y_UNIT_TEST(FindIndexTest) { + TVectorNoCopy v; + v.push_back(1); + v.push_back(2); + v.push_back(3); + + UNIT_ASSERT_EQUAL(0, FindIndex(v, 1)); + UNIT_ASSERT_EQUAL(1, FindIndex(v, 2)); + UNIT_ASSERT_EQUAL(2, FindIndex(v, 3)); + UNIT_ASSERT_EQUAL(NPOS, FindIndex(v, 42)); + + int array[3] = {1, 2, 3}; + + UNIT_ASSERT_EQUAL(0, FindIndex(array, 1)); + UNIT_ASSERT_EQUAL(1, FindIndex(array, 2)); + UNIT_ASSERT_EQUAL(2, FindIndex(array, 3)); + UNIT_ASSERT_EQUAL(NPOS, FindIndex(array, 42)); + + TVector empty; + UNIT_ASSERT_EQUAL(NPOS, FindIndex(empty, 0)); + } + + Y_UNIT_TEST(FindIndexIfTest) { + TVectorNoCopy v; + v.push_back(1); + v.push_back(2); + v.push_back(3); + + UNIT_ASSERT_EQUAL(0, FindIndexIf(v, [](int x) { return x == 1; })); + UNIT_ASSERT_EQUAL(1, FindIndexIf(v, [](int x) { return x == 2; })); + UNIT_ASSERT_EQUAL(2, FindIndexIf(v, [](int x) { return x == 3; })); + UNIT_ASSERT_EQUAL(NPOS, FindIndexIf(v, [](int x) { return x == 42; })); + + int array[3] = {1, 2, 3}; + + UNIT_ASSERT_EQUAL(0, FindIndexIf(array, [](int x) { return x == 1; })); + UNIT_ASSERT_EQUAL(1, FindIndexIf(array, [](int x) { return x == 2; })); + UNIT_ASSERT_EQUAL(2, FindIndexIf(array, [](int x) { return x == 3; })); + UNIT_ASSERT_EQUAL(NPOS, FindIndexIf(array, [](int x) { return x == 42; })); + + TVector empty; + UNIT_ASSERT_EQUAL(NPOS, FindIndexIf(empty, [](int x) { return x == 3; })); + } + + Y_UNIT_TEST(SortUniqueTest) { + { + TVector v; + SortUnique(v); + UNIT_ASSERT_EQUAL(v, TVector()); + } + + { + const char* ar[] = {"345", "3", "123", "2", "23", "3", "2"}; + TVector v(ar, ar + Y_ARRAY_SIZE(ar)); + SortUnique(v); + + const char* suAr[] = {"123", "2", "23", "3", "345"}; + TVector suV(suAr, suAr + Y_ARRAY_SIZE(suAr)); + + UNIT_ASSERT_EQUAL(v, suV); + } + } + + Y_UNIT_TEST(EraseTest) { + TVector data = {5, 4, 3, 2, 1, 0}; + TVector expected = {5, 4, 2, 1, 0}; + Erase(data, 3); + UNIT_ASSERT_EQUAL(data, expected); + } + + Y_UNIT_TEST(EraseIfTest) { + TVector data = {5, 4, 3, 2, 1, 0}; + TVector expected = {2, 1, 0}; + EraseIf(data, [](int i) { return i >= 3; }); + UNIT_ASSERT_EQUAL(data, expected); + } + + Y_UNIT_TEST(EraseNodesIfTest) { + TMap map{{1, 1}, {2, 2}, {3, 5}}; + TMap expectedMap{{1, 1}}; + EraseNodesIf(map, [](auto p) { return p.first >= 2; }); + UNIT_ASSERT_EQUAL(map, expectedMap); + + TMultiMap multiMap{{1, 1}, {1, 3}, {2, 2}, {3, 5}}; + TMultiMap expectedMultiMap{{1, 1}, {1, 3}}; + EraseNodesIf(multiMap, [](auto p) { return p.first >= 2; }); + UNIT_ASSERT_EQUAL(multiMap, expectedMultiMap); + + TSet set{1, 2, 3, 4, 5, 6, 7}; + TSet expectedSet{1, 3, 5, 7}; + EraseNodesIf(set, [](int i) { return i % 2 == 0; }); + UNIT_ASSERT_EQUAL(set, expectedSet); + + TMultiSet multiSet{1, 1, 2, 3, 4, 4, 4, 5, 5, 5, 6, 7}; + TMultiSet expectedMultiSet{1, 1, 3, 5, 5, 5, 7}; + EraseNodesIf(multiSet, [](int i) { return i % 2 == 0; }); + UNIT_ASSERT_EQUAL(multiSet, expectedMultiSet); + + THashMap hashMap{{1, 0}, {3, 0}, {4, 0}, {10, 0}, {2, 0}, {5, 2}}; + THashMap expectedHashMap{{1, 0}, {3, 0}, {5, 2}}; + EraseNodesIf(hashMap, [](auto p) { return p.first % 2 == 0; }); + UNIT_ASSERT_EQUAL(hashMap, expectedHashMap); + + THashMultiMap hashMultiMap{{1, 0}, {3, 0}, {4, 0}, {10, 0}, {2, 0}, {5, 0}, {1, 0}, {1, 0}, {2, 0}, {2, 2}}; + THashMultiMap expectedHashMultiMap{{1, 0}, {1, 0}, {1, 0}, {3, 0}, {5, 0}}; + EraseNodesIf(hashMultiMap, [](auto p) { return p.first % 2 == 0; }); + UNIT_ASSERT_EQUAL(hashMultiMap, expectedHashMultiMap); + } + + Y_UNIT_TEST(NthElementTest) { + { + TVector v; + NthElement(v.begin(), v.begin(), v.end()); + UNIT_ASSERT_EQUAL(v, TVector()); + } + + { + int data[] = {3, 2, 1, 4, 6, 5, 7, 9, 8}; + TVector testVector(data, data + Y_ARRAY_SIZE(data)); + + size_t medianInd = testVector.size() / 2; + + NthElement(testVector.begin(), testVector.begin() + medianInd, testVector.end()); + UNIT_ASSERT_EQUAL(testVector[medianInd], 5); + + NthElement(testVector.begin(), testVector.begin() + medianInd, testVector.end(), [](int lhs, int rhs) { return lhs > rhs; }); + UNIT_ASSERT_EQUAL(testVector[medianInd], 5); + } + + { + const char* data[] = {"3", "234", "1231", "333", "545345", "11", "111", "55", "66"}; + TVector testVector(data, data + Y_ARRAY_SIZE(data)); + + size_t medianInd = testVector.size() / 2; + NthElement(testVector.begin(), testVector.begin() + medianInd, testVector.end()); + + auto median = testVector.begin() + medianInd; + for (auto it0 = testVector.begin(); it0 != median; ++it0) { + for (auto it1 = median; it1 != testVector.end(); ++it1) { + UNIT_ASSERT(*it0 <= *it1); + } + } + } + } + + Y_UNIT_TEST(BinarySearchTest) { + { + TVector v; + bool test = BinarySearch(v.begin(), v.end(), "test"); + UNIT_ASSERT_EQUAL(test, false); + } + + { + int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + bool test = BinarySearch(data, data + Y_ARRAY_SIZE(data), 2); + UNIT_ASSERT_EQUAL(test, true); + + test = BinarySearch(data, data + Y_ARRAY_SIZE(data), 10); + UNIT_ASSERT_EQUAL(test, false); + } + + { + TVector data = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + + bool test = BinarySearch(data.begin(), data.end(), (size_t)9, TGreater()); + UNIT_ASSERT_EQUAL(test, true); + + test = BinarySearch(data.begin(), data.end(), (size_t)11, TGreater()); + UNIT_ASSERT_EQUAL(test, false); + + test = BinarySearch(data.rbegin(), data.rend(), (size_t)1); + UNIT_ASSERT_EQUAL(test, true); + } + } + + Y_UNIT_TEST(EqualRangeTest) { + { + TVector v; + using PairOfVector = std::pair::iterator, TVector::iterator>; + PairOfVector tmp = EqualRange(v.begin(), v.end(), "tmp"); + + UNIT_ASSERT_EQUAL(tmp.first, tmp.second); + UNIT_ASSERT_EQUAL(tmp.first, v.end()); + } + + { + int data[] = {1, 1, 1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 5}; + using PairOfInt = std::pair; + PairOfInt tmp = EqualRange(data, data + Y_ARRAY_SIZE(data), 3); + + UNIT_ASSERT_EQUAL(tmp.second - tmp.first, 4); + UNIT_ASSERT_EQUAL(tmp.first - data, 7); + UNIT_ASSERT_EQUAL(data + Y_ARRAY_SIZE(data) - tmp.second, 2); + } + + { + TVector data = {9, 9, 8, 8, 8, 5, 4, 3, 3, 0, 0}; + + using PairOfVector = std::pair::iterator, TVector::iterator>; + PairOfVector tmp = EqualRange(data.begin(), data.end(), 8, TGreater()); + + UNIT_ASSERT_EQUAL(tmp.first - data.begin(), 2); + UNIT_ASSERT_EQUAL(tmp.second - tmp.first, 3); + + using PairOfVectorReverse = std::pair::reverse_iterator, TVector::reverse_iterator>; + PairOfVectorReverse tmpR = EqualRange(data.rbegin(), data.rend(), (size_t)0); + + UNIT_ASSERT_EQUAL(tmpR.first, data.rbegin()); + UNIT_ASSERT_EQUAL(tmpR.second - tmpR.first, 2); + } + } + + Y_UNIT_TEST(AdjacentFindTest) { + TVector v0; + UNIT_ASSERT_EQUAL(AdjacentFind(v0), v0.end()); + + TVector v1 = {1}; + UNIT_ASSERT_EQUAL(AdjacentFind(v1), v1.end()); + + const int v2[] = {8, 7, 6, 6, 5, 5, 5, 4, 3, 2, 1}; + UNIT_ASSERT_EQUAL(AdjacentFind(v2), std::begin(v2) + 2); + + TVector v3 = {"six", "five", "four", "three", "two", "one"}; + UNIT_ASSERT_EQUAL(AdjacentFind(v3), v3.end()); + + TVector v4 = {1, 1, 1, 1, 1}; + for (;;) { + if (auto it = AdjacentFind(v4); it == v4.end()) { + break; + } else { + *it += 1; + } + } + UNIT_ASSERT_VALUES_EQUAL(v4, (TVector{5, 4, 3, 2, 1})); + } + + Y_UNIT_TEST(AdjacentFindByTest) { + TVector v0; + UNIT_ASSERT_EQUAL(AdjacentFindBy(v0, std::negate()), v0.end()); + + TVector v1 = {1}; + UNIT_ASSERT_EQUAL(AdjacentFindBy(v1, std::negate()), v1.end()); + + const int v2[] = {8, 7, 6, 6, 5, 5, 5, 4, 3, 2, 1}; + UNIT_ASSERT_EQUAL(AdjacentFindBy(v2, std::negate()), std::begin(v2) + 2); + UNIT_ASSERT_EQUAL(AdjacentFindBy(v2, [](const auto& e) { return e / 8; }), std::begin(v2) + 1); + + TVector v3 = {"six", "five", "four", "three", "two", "one"}; + UNIT_ASSERT_EQUAL(AdjacentFind(v3), v3.end()); + UNIT_ASSERT_EQUAL(AdjacentFindBy(v3, std::mem_fn(&TStringBuf::size)), v3.begin() + 1); + + TVector v4 = {101, 201, 301, 401, 501}; + for (;;) { + if (auto it = AdjacentFindBy(v4, [](int a) { return a % 10; }); it == v4.end()) { + break; + } else { + *it += 1; + } + } + UNIT_ASSERT_VALUES_EQUAL(v4, (TVector{105, 204, 303, 402, 501})); + } + + Y_UNIT_TEST(IsSortedTest) { + TVector v0; + UNIT_ASSERT_VALUES_EQUAL(IsSorted(v0.begin(), v0.end()), true); + + TVector v1 = {1, 2, 3, 4, 5, 5, 5, 6, 6, 7, 8}; + UNIT_ASSERT_VALUES_EQUAL(IsSorted(v1.begin(), v1.end()), true); + UNIT_ASSERT_VALUES_EQUAL(IsSorted(v1.begin(), v1.end(), TLess()), true); + UNIT_ASSERT_VALUES_EQUAL(IsSorted(v1.begin(), v1.end(), TGreater()), false); + + TVector v2 = {1, 2, 1}; + UNIT_ASSERT_VALUES_EQUAL(IsSorted(v2.begin(), v2.end()), false); + UNIT_ASSERT_VALUES_EQUAL(IsSorted(v2.begin(), v2.end(), TLess()), false); + UNIT_ASSERT_VALUES_EQUAL(IsSorted(v2.begin(), v2.end(), TGreater()), false); + } + + Y_UNIT_TEST(IsSortedByTest) { + TVector v0; + UNIT_ASSERT_VALUES_EQUAL(IsSortedBy(v0.begin(), v0.end(), std::negate()), true); + UNIT_ASSERT_VALUES_EQUAL(IsSortedBy(v0, std::negate()), true); + + TVector v1 = {1}; + UNIT_ASSERT_VALUES_EQUAL(IsSortedBy(v1.begin(), v1.end(), std::negate()), true); + UNIT_ASSERT_VALUES_EQUAL(IsSortedBy(v1, std::negate()), true); + + TVector v2 = {8, 7, 6, 6, 5, 5, 5, 4, 3, 2, 1}; + UNIT_ASSERT_VALUES_EQUAL(IsSortedBy(v2.begin(), v2.end(), std::negate()), true); + UNIT_ASSERT_VALUES_EQUAL(IsSortedBy(v2, std::negate()), true); + + TVector v3 = {1, 2, 1}; + UNIT_ASSERT_VALUES_EQUAL(IsSortedBy(v3.begin(), v3.end(), std::negate()), false); + UNIT_ASSERT_VALUES_EQUAL(IsSortedBy(v3, std::negate()), false); + } + + Y_UNIT_TEST(SortTestTwoIterators) { + TVector collection = {10, 2, 7}; + Sort(collection.begin(), collection.end()); + TVector expected = {2, 7, 10}; + UNIT_ASSERT_VALUES_EQUAL(collection, expected); + } + + Y_UNIT_TEST(SortTestTwoIteratorsAndComparator) { + TVector collection = {10, 2, 7}; + Sort(collection.begin(), collection.end(), [](int l, int r) { return l > r; }); + TVector expected = {10, 7, 2}; + UNIT_ASSERT_VALUES_EQUAL(collection, expected); + } + + Y_UNIT_TEST(SortTestContainer) { + TVector collection = {10, 2, 7}; + Sort(collection); + TVector expected = {2, 7, 10}; + UNIT_ASSERT_VALUES_EQUAL(collection, expected); + } + + Y_UNIT_TEST(SortTestContainerAndComparator) { + TVector collection = {10, 2, 7}; + Sort(collection, [](int l, int r) { return l > r; }); + TVector expected = {10, 7, 2}; + UNIT_ASSERT_VALUES_EQUAL(collection, expected); + } + + Y_UNIT_TEST(StableSortTestTwoIterators) { + TVector collection = {10, 2, 7}; + StableSort(collection.begin(), collection.end()); + TVector expected = {2, 7, 10}; + UNIT_ASSERT_VALUES_EQUAL(collection, expected); + } + + Y_UNIT_TEST(StableSortTestTwoIteratorsAndComparator) { + TVector collection = {404, 101, 106, 203, 102, 205, 401}; + StableSort(collection.begin(), collection.end(), [](int l, int r) { return (l / 100) < (r / 100); }); + TVector expected = {101, 106, 102, 203, 205, 404, 401}; + UNIT_ASSERT_VALUES_EQUAL(collection, expected); + } + + Y_UNIT_TEST(StableSortTestContainer) { + TVector collection = {10, 2, 7}; + StableSort(collection); + TVector expected = {2, 7, 10}; + UNIT_ASSERT_VALUES_EQUAL(collection, expected); + } + + Y_UNIT_TEST(StableSortTestContainerAndComparator) { + TVector collection = {404, 101, 106, 203, 102, 205, 401}; + StableSort(collection, [](int l, int r) { return (l / 100) < (r / 100); }); + TVector expected = {101, 106, 102, 203, 205, 404, 401}; + UNIT_ASSERT_VALUES_EQUAL(collection, expected); + } + + Y_UNIT_TEST(SortByTest) { + TVector collection = {10, 2, 7}; + SortBy(collection, [](int x) { return -x; }); + TVector expected = {10, 7, 2}; + UNIT_ASSERT_VALUES_EQUAL(collection, expected); + } + + Y_UNIT_TEST(StableSortByTest) { + TVector collection = {404, 101, 106, 203, 102, 205, 401}; + StableSortBy(collection, [](int x) { return x / 100; }); + TVector expected = {101, 106, 102, 203, 205, 404, 401}; + UNIT_ASSERT_VALUES_EQUAL(collection, expected); + } + + Y_UNIT_TEST(SortUniqueByTest) { + TVector collection = {404, 101, 101, 203, 101, 203, 404}; + StableSortUniqueBy(collection, [](int x) { return x / 100; }); + TVector expected = {101, 203, 404}; + UNIT_ASSERT_VALUES_EQUAL(collection, expected); + } + + Y_UNIT_TEST(StableSortUniqueByTest) { + TVector collection = {404, 101, 106, 203, 102, 205, 401}; + StableSortUniqueBy(collection, [](int x) { return x / 100; }); + TVector expected = {101, 203, 404}; + UNIT_ASSERT_VALUES_EQUAL(collection, expected); + } + + Y_UNIT_TEST(IotaTest) { + TVector v(10); + + Iota(v.begin(), v.end(), 0); + UNIT_ASSERT_VALUES_EQUAL(v[0], 0); + UNIT_ASSERT_VALUES_EQUAL(v[5], 5); + UNIT_ASSERT_VALUES_EQUAL(v[9], 9); + + Iota(v.begin() + 2, v.begin() + 5, 162); + UNIT_ASSERT_VALUES_EQUAL(v[0], 0); + UNIT_ASSERT_VALUES_EQUAL(v[3], 163); + UNIT_ASSERT_VALUES_EQUAL(v[9], 9); + } + + Y_UNIT_TEST(CopyNTest) { + int data[] = {1, 2, 3, 4, 8, 7, 6, 5}; + const size_t vSize = 10; + TVector result(10, 0); + size_t toCopy = 5; + + TVector::iterator iter = CopyN(data, toCopy, result.begin()); + UNIT_ASSERT_VALUES_EQUAL(iter - result.begin(), toCopy); + UNIT_ASSERT_VALUES_EQUAL(result.size(), 10); + for (size_t idx = 0; idx < toCopy; ++idx) { + UNIT_ASSERT_VALUES_EQUAL(data[idx], result[idx]); + } + for (size_t idx = toCopy; idx < vSize; ++idx) { + UNIT_ASSERT_VALUES_EQUAL(result[idx], 0); + } + + toCopy = 8; + const size_t start = 1; + result.assign(vSize, 0); + iter = CopyN(data, toCopy, result.begin() + start); + UNIT_ASSERT_VALUES_EQUAL(iter - result.begin(), start + toCopy); + for (size_t idx = 0; idx < start; ++idx) { + UNIT_ASSERT_VALUES_EQUAL(result[idx], 0); + } + for (size_t idx = 0; idx < toCopy; ++idx) { + UNIT_ASSERT_VALUES_EQUAL(result[start + idx], data[idx]); + } + for (size_t idx = start + toCopy; idx < vSize; ++idx) { + UNIT_ASSERT_VALUES_EQUAL(result[idx], 0); + } + } + + Y_UNIT_TEST(CopyIfTest) { + const size_t count = 9; + int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + const size_t vSize = 10; + TVector v(vSize, 0); + + TVector::iterator iter = CopyIf(data, data + count, v.begin(), [](int x) { return !(x % 3); }); + UNIT_ASSERT_VALUES_EQUAL(v.size(), vSize); + UNIT_ASSERT_VALUES_EQUAL(iter - v.begin(), 3); + v.resize(iter - v.begin()); + for (size_t idx = 0; idx < v.size(); ++idx) { + UNIT_ASSERT_VALUES_EQUAL(v[idx], 3 * (idx + 1)); + } + } + + Y_UNIT_TEST(MinMaxElementTest) { + TVector v(10); + Iota(v.begin(), v.end(), 0); + UNIT_ASSERT_EQUAL(*MinMaxElement(v.begin(), v.end()).first, 0); + UNIT_ASSERT_EQUAL(*MinMaxElement(v.begin(), v.end()).second, 9); + + v[3] = -2; + v[7] = 11; + UNIT_ASSERT_EQUAL(*MinMaxElement(v.begin(), v.end()).first, -2); + UNIT_ASSERT_EQUAL(*MinMaxElement(v.begin(), v.end()).second, 11); + } + + Y_UNIT_TEST(MinMaxTest) { + std::pair p1 = MinMax(5, 12); + UNIT_ASSERT_EQUAL(p1.first, 5); + UNIT_ASSERT_EQUAL(p1.second, 12); + + std::pair p2 = MinMax(TString("test"), TString("data")); + UNIT_ASSERT_EQUAL(p2.first, TString("data")); + UNIT_ASSERT_EQUAL(p2.second, TString("test")); + } + + Y_UNIT_TEST(TestMaxElementBy) { + const int array[] = {1, 2, 5, 3, 4, 5}; + UNIT_ASSERT_VALUES_EQUAL(*MaxElementBy(array, [](int x) { + return x * x; + }), 5); + + const TVector vec(array, array + Y_ARRAY_SIZE(array)); + UNIT_ASSERT_VALUES_EQUAL(*MaxElementBy(vec, [](int x) { + return -1.0 * x; + }), 1); + + int arrayMutable[] = {1, 2, 5, 3, 4, 5}; + auto maxPtr = MaxElementBy(arrayMutable, [](int x) { return x; }); + *maxPtr += 100; + UNIT_ASSERT_VALUES_EQUAL(*maxPtr, 105); + + auto identity = [](char x) { + return x; + }; + auto singleElementSequence = {'z'}; + UNIT_ASSERT_VALUES_EQUAL(*MaxElementBy(singleElementSequence, identity), 'z'); + + const TString strings[] = {"one", "two", "three", "four"}; + UNIT_ASSERT_STRINGS_EQUAL(*MaxElementBy(strings, [](TString s) { return s.size(); }), "three"); + } + + Y_UNIT_TEST(TestMinElementBy) { + const int array[] = {2, 3, 4, 1, 5}; + UNIT_ASSERT_VALUES_EQUAL(*MinElementBy(array, [](int x) -> char { + return 'a' + x; + }), 1); + + const TVector vec(std::begin(array), std::end(array)); + UNIT_ASSERT_VALUES_EQUAL(*MinElementBy(vec, [](int x) { + return -x; + }), 5); + + int arrayMutable[] = {1, 2, 5, 3, 4, 5}; + auto minPtr = MinElementBy(arrayMutable, [](int x) { return x; }); + *minPtr += 100; + UNIT_ASSERT_VALUES_EQUAL(*minPtr, 101); + + auto identity = [](char x) { + return x; + }; + auto singleElementSequence = {'z'}; + UNIT_ASSERT_VALUES_EQUAL(*MinElementBy(singleElementSequence, identity), 'z'); + + const TVector strings = {"one", "two", "three", "four"}; + auto stringLength = [](TStringBuf s) { + return s.size(); + }; + UNIT_ASSERT_STRINGS_EQUAL(*MinElementBy(strings, stringLength), "one"); + UNIT_ASSERT_STRINGS_EQUAL(*MinElementBy(strings.rbegin(), strings.rend(), stringLength), "two"); + } + + Y_UNIT_TEST(MaxElementByReturnsEndForEmptyRange) { + const TVector empty; + UNIT_ASSERT_EQUAL(MaxElementBy(empty, [](int) { return 0; }), empty.end()); + } + + Y_UNIT_TEST(MaxElementByDoesntCallFunctorForEmptyRange) { + const TVector empty; + auto functor = [](int) { + UNIT_ASSERT(false); + return 0; + }; + MaxElementBy(empty, functor); + } + + Y_UNIT_TEST(MinElementByReturnsEndForEmptyRange) { + const TVector empty; + UNIT_ASSERT_EQUAL(MinElementBy(empty, [](int) { return 0; }), empty.end()); + } + + Y_UNIT_TEST(MinElementByDoesntCallFunctorForEmptyRange) { + const TVector empty; + auto functor = [](int) { + UNIT_ASSERT(false); + return 0; + }; + MinElementBy(empty, functor); + } + + Y_UNIT_TEST(TestApplyToMany) { + int res = 0; + ApplyToMany([&res](auto v) { res += v; }, 1, 2, 3, 4, 5); + UNIT_ASSERT_EQUAL(res, 15); + + struct TVisitor { + TVisitor(int& acc) + : Acc(acc) + { + } + void operator()(const TString& s) { + Acc += s.size(); + } + void operator()(int v) { + Acc += v * 2; + } + int& Acc; + }; + TString s{"8-800-555-35-35"}; + ApplyToMany(TVisitor{res = 0}, 1, s, 5, s); + UNIT_ASSERT_EQUAL(res, 12 + 2 * static_cast(s.size())); + } + + Y_UNIT_TEST(TestTupleForEach) { + ForEach(std::tuple<>{}, [&](auto) { UNIT_ASSERT(false); }); + auto t = std::make_tuple(5, 6, 2, 3, 6); + ForEach(t, [](auto& v) { v *= -1; }); + UNIT_ASSERT_EQUAL(t, std::make_tuple(-5, -6, -2, -3, -6)); + } + + Y_UNIT_TEST(TestTupleAllOf) { + UNIT_ASSERT(AllOf(std::tuple<>{}, [](auto) { return false; })); + UNIT_ASSERT(!AllOf(std::make_tuple(1, 2, 0, 4, 5), [&](auto v) { UNIT_ASSERT_LT(v, 3); return 0 != v; })); + UNIT_ASSERT(AllOf(std::make_tuple(1, 2, 3, 4, 5), [](auto v) { return 0 != v; })); + { + auto pred = std::function([x = TVector(1, 0)](auto v) { return x.front() != v; }); + UNIT_ASSERT(AllOf(std::make_tuple(1, 2), pred)); + UNIT_ASSERT(AllOf(std::make_tuple(1, 2), pred)); + } + { + auto ts = std::make_tuple(TString{"foo"}, TString{"bar"}); + auto pred = [](auto s) { return s.size() == 3; }; + UNIT_ASSERT_VALUES_EQUAL(AllOf(ts, pred), AllOf(ts, pred)); + } + } + + Y_UNIT_TEST(TestTupleAnyOf) { + UNIT_ASSERT(!AnyOf(std::tuple<>{}, [](auto) { return true; })); + UNIT_ASSERT(AnyOf(std::make_tuple(0, 1, 2, 3, 4), [&](auto v) { UNIT_ASSERT_LT(v, 2); return 1 == v; })); + UNIT_ASSERT(AnyOf(std::make_tuple(1, 2, 3, 4, 5), [](auto v) { return 5 == v; })); + auto pred = std::function([x = TVector(1, 0)](auto v) { return x.front() == v; }); + UNIT_ASSERT(!AnyOf(std::make_tuple(1, 2), pred)); + UNIT_ASSERT(!AnyOf(std::make_tuple(1, 2), pred)); + { + auto ts = std::make_tuple(TString{"f"}, TString{"bar"}); + auto pred = [](auto s) { return s.size() == 3; }; + UNIT_ASSERT_VALUES_EQUAL(AnyOf(ts, pred), AnyOf(ts, pred)); + } + } + + Y_UNIT_TEST(FindIfForContainer) { + using std::begin; + using std::end; + + int array[] = {1, 2, 3, 4, 5}; + UNIT_ASSERT_EQUAL(FindIf(array, [](int x) { return x == 1; }), begin(array)); + UNIT_ASSERT_EQUAL(FindIf(array, [](int x) { return x > 5; }), end(array)); + + TVector vector = {1, 2, 3, 4, 5}; + UNIT_ASSERT_EQUAL(FindIf(vector, [](int x) { return x == 1; }), begin(vector)); + UNIT_ASSERT_EQUAL(FindIf(vector, [](int x) { return x > 5; }), end(vector)); + + // Compilability test. Check if the returned iterator is non const + auto iter = FindIf(vector, [](int x) { return x == 1; }); + *iter = 5; + + // Compilability test. Check if the returned iterator is const. Should not compile + const TVector constVector = {1, 2, 3, 4, 5}; + auto constIter = FindIf(constVector, [](int x) { return x == 1; }); + Y_UNUSED(constIter); + // *constIter = 5; + } + + struct TRange { + }; + + const TRange* begin(const TRange& r) { + return &r; + } + + const TRange* end(const TRange& r) { + return &r + 1; + } + + Y_UNIT_TEST(FindIfForUserType) { + // Compileability test. Should work for user types with begin/end overloads + TRange range; + auto i = FindIf(range, [](auto) { return false; }); + Y_UNUSED(i); + } + + Y_UNIT_TEST(TestLowerBoundBy) { + using TIntPairs = TVector>; + + auto data = TIntPairs{{1, 5}, {3, 2}, {3, 4}, {8, 0}, {5, 4}}; + auto getKey = [](const auto& x) { return x.second; }; + + StableSortBy(data, getKey); + + auto it = LowerBoundBy(data.begin(), data.end(), 4, getKey); + UNIT_ASSERT(it != data.end()); + UNIT_ASSERT_EQUAL(it->second, 4); + UNIT_ASSERT_EQUAL(it->first, 3); + + UNIT_ASSERT(it > data.begin()); + UNIT_ASSERT_EQUAL((it - 1)->second, 2); + + UNIT_ASSERT((it + 1) < data.end()); + UNIT_ASSERT_EQUAL((it + 1)->second, 4); + } + + Y_UNIT_TEST(TestUpperBoundBy) { + using TIntPairs = TVector>; + + auto data = TIntPairs{{1, 5}, {3, 2}, {3, 4}, {8, 0}, {5, 4}}; + auto getKey = [](const auto& x) { return x.second; }; + + StableSortBy(data, getKey); + + auto it = UpperBoundBy(data.begin(), data.end(), 4, getKey); + UNIT_ASSERT(it != data.end()); + UNIT_ASSERT_EQUAL(it->second, 5); + UNIT_ASSERT_EQUAL(it->first, 1); + + UNIT_ASSERT(it > data.begin()); + UNIT_ASSERT_EQUAL((it - 1)->second, 4); + + UNIT_ASSERT((it + 1) == data.end()); + } + + Y_UNIT_TEST(TestFindInContainer) { + std::vector v = {1, 2, 1000, 15, 100}; + UNIT_ASSERT(Find(v, 5) == v.end()); + UNIT_ASSERT(Find(v, 1) == v.begin()); + UNIT_ASSERT(Find(v, 100) == v.end() - 1); + } + + Y_UNIT_TEST(AccumulateWithBinOp) { + std::vector v = {1, 2, 777}; + UNIT_ASSERT_VALUES_EQUAL(TString("begin;1;2;777"), Accumulate(v, TString("begin"), [](auto&& a, auto& b) { return a + ";" + ToString(b); })); + } +} diff --git a/util/generic/array_ref.cpp b/util/generic/array_ref.cpp new file mode 100644 index 00000000000..e5b6a3c1a65 --- /dev/null +++ b/util/generic/array_ref.cpp @@ -0,0 +1 @@ +#include "array_ref.h" diff --git a/util/generic/array_ref.h b/util/generic/array_ref.h new file mode 100644 index 00000000000..83d225c6ff7 --- /dev/null +++ b/util/generic/array_ref.h @@ -0,0 +1,283 @@ +#pragma once + +#include + +#include +#include +#include + +/** + * `TArrayRef` works pretty much like `std::span` with dynamic extent, presenting + * an array-like interface into a contiguous sequence of objects. + * + * It can be used at interface boundaries instead of `TVector` or + * pointer-size pairs, and is actually a preferred way to pass contiguous data + * into functions. + * + * Note that `TArrayRef` can be auto-constructed from any contiguous container + * (with `size` and `data` members), and thus you don't have to change client code + * when switching over from passing `TVector` to `TArrayRef`. + * + * Note that `TArrayRef` has the same const-semantics as raw pointers: + * - `TArrayRef` is a non-const reference to non-const data (like `T*`); + * - `TArrayRef` is a non-const reference to const data (like `const T*`); + * - `const TArrayRef` is a const reference to non-const data (like `T* const`); + * - `const TArrayRef` is a const reference to const data (like `const T* const`). + */ +template +class TArrayRef { +public: + using iterator = T*; + using const_iterator = const T*; + using reference = T&; + using const_reference = const T&; + using value_type = T; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + constexpr inline TArrayRef() noexcept + : T_(nullptr) + , S_(0) + { + } + + constexpr inline TArrayRef(T* data, size_t len) noexcept + : T_(data) + , S_(len) + { + } + + constexpr inline TArrayRef(T* begin, T* end) noexcept + : T_(begin) + , S_(end - begin) + { + } + + constexpr inline TArrayRef(std::initializer_list list) noexcept + : T_(list.begin()) + , S_(list.size()) + { + } + + template + constexpr inline TArrayRef(Container&& container, decltype(std::declval() = container.data(), nullptr) = nullptr) noexcept + : T_(container.data()) + , S_(container.size()) + { + static_assert( + sizeof(decltype(*container.data())) == sizeof(T), + "Attempt to create TArrayRef from a container of elements with a different size"); + } + + template + constexpr inline TArrayRef(T (&array)[N]) noexcept + : T_(array) + , S_(N) + { + } + + template , std::remove_const_t>::value>> + bool operator==(const TArrayRef& other) const noexcept { + return (S_ == other.size()) && std::equal(begin(), end(), other.begin()); + } + + constexpr inline T* data() const noexcept { + return T_; + } + + constexpr inline size_t size() const noexcept { + return S_; + } + + constexpr size_t size_bytes() const noexcept { + return (size() * sizeof(T)); + } + + constexpr inline bool empty() const noexcept { + return (S_ == 0); + } + + constexpr inline iterator begin() const noexcept { + return T_; + } + + constexpr inline iterator end() const noexcept { + return (T_ + S_); + } + + constexpr inline const_iterator cbegin() const noexcept { + return T_; + } + + constexpr inline const_iterator cend() const noexcept { + return (T_ + S_); + } + + constexpr inline reverse_iterator rbegin() const noexcept { + return reverse_iterator(T_ + S_); + } + + constexpr inline reverse_iterator rend() const noexcept { + return reverse_iterator(T_); + } + + constexpr inline const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(T_ + S_); + } + + constexpr inline const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(T_); + } + + constexpr inline reference front() const noexcept { + return *T_; + } + + inline reference back() const noexcept { + Y_ASSERT(S_ > 0); + + return *(end() - 1); + } + + inline reference operator[](size_t n) const noexcept { + Y_ASSERT(n < S_); + + return *(T_ + n); + } + + inline reference at(size_t n) const { + if (n >= S_) { + throw std::out_of_range("array ref range error"); + } + + return (*this)[n]; + } + + constexpr inline explicit operator bool() const noexcept { + return (S_ > 0); + } + + /** + * Obtains a ref that is a view over the first `count` elements of this TArrayRef. + * + * The behavior is undefined if count > size(). + */ + TArrayRef first(size_t count) const { + Y_ASSERT(count <= size()); + return TArrayRef(data(), count); + } + + /** + * Obtains a ref that is a view over the last `count` elements of this TArrayRef. + * + * The behavior is undefined if count > size(). + */ + TArrayRef last(size_t count) const { + Y_ASSERT(count <= size()); + return TArrayRef(end() - count, end()); + } + + /** + * Obtains a ref that is a view over the `count` elements of this TArrayRef starting at `offset`. + * + * The behavior is undefined in either offset or count is out of range. + */ + TArrayRef subspan(size_t offset) const { + Y_ASSERT(offset <= size()); + return TArrayRef(data() + offset, size() - offset); + } + + TArrayRef subspan(size_t offset, size_t count) const { + Y_ASSERT(offset + count <= size()); + return TArrayRef(data() + offset, count); + } + + TArrayRef Slice(size_t offset) const { + return subspan(offset); + } + + TArrayRef Slice(size_t offset, size_t size) const { + return subspan(offset, size); + } + + /* FIXME: + * This method is placed here for backward compatibility only and should be removed. + * Keep in mind that it's behavior is different from Slice(): + * SubRegion() never throws. It returns empty TArrayRef in case of invalid input. + * + * DEPRECATED. DO NOT USE. + */ + TArrayRef SubRegion(size_t offset, size_t size) const { + if (size == 0 || offset >= S_) { + return TArrayRef(); + } + + if (size > S_ - offset) { + size = S_ - offset; + } + + return TArrayRef(T_ + offset, size); + } + + constexpr inline yssize_t ysize() const noexcept { + return static_cast(this->size()); + } + +private: + T* T_; + size_t S_; +}; + +/** + * Obtains a view to the object representation of the elements of the TArrayRef arrayRef. + * + * Named as its std counterparts, std::as_bytes. + */ +template +TArrayRef as_bytes(TArrayRef arrayRef) noexcept { + return TArrayRef( + reinterpret_cast(arrayRef.data()), + arrayRef.size_bytes()); +} + +/** + * Obtains a view to the writable object representation of the elements of the TArrayRef arrayRef. + * + * Named as its std counterparts, std::as_writable_bytes. + */ +template +TArrayRef as_writable_bytes(TArrayRef arrayRef) noexcept { + return TArrayRef( + reinterpret_cast(arrayRef.data()), + arrayRef.size_bytes()); +} + +template +constexpr TArrayRef MakeArrayRef(const Range& range) { + return TArrayRef(range); +} + +template +constexpr TArrayRef MakeArrayRef(Range& range) { + return TArrayRef(range); +} + +template +constexpr TArrayRef MakeConstArrayRef(const Range& range) { + return TArrayRef(range); +} + +template +constexpr TArrayRef MakeConstArrayRef(Range& range) { + return TArrayRef(range); +} + +template +constexpr TArrayRef MakeArrayRef(T* data, size_t size) { + return TArrayRef(data, size); +} + +template +constexpr TArrayRef MakeArrayRef(T* begin, T* end) { + return TArrayRef(begin, end); +} diff --git a/util/generic/array_ref.pxd b/util/generic/array_ref.pxd new file mode 100644 index 00000000000..a4ba42e357a --- /dev/null +++ b/util/generic/array_ref.pxd @@ -0,0 +1,25 @@ +from libcpp cimport bool as bool_t + + +cdef extern from "util/generic/array_ref.h" nogil: + cdef cppclass TArrayRef[T]: + TArrayRef(...) + + T& operator[](size_t) + + bool_t empty() + T* data() + size_t size() + T* begin() + T* end() + + cdef cppclass TConstArrayRef[T]: + TConstArrayRef(...) + + const T& operator[](size_t) + + bool_t empty() + const T* data() + size_t size() + const T* begin() + const T* end() diff --git a/util/generic/array_ref_ut.cpp b/util/generic/array_ref_ut.cpp new file mode 100644 index 00000000000..4c8eaf7135a --- /dev/null +++ b/util/generic/array_ref_ut.cpp @@ -0,0 +1,322 @@ +#include "array_ref.h" + +#include + +Y_UNIT_TEST_SUITE(TestArrayRef) { + Y_UNIT_TEST(TestDefaultConstructor) { + TArrayRef defaulted; + UNIT_ASSERT_VALUES_EQUAL(defaulted.data(), nullptr); + UNIT_ASSERT_VALUES_EQUAL(defaulted.size(), 0u); + } + + Y_UNIT_TEST(TestConstructorFromArray) { + int x[] = {10, 20, 30}; + TArrayRef ref(x); + UNIT_ASSERT_VALUES_EQUAL(3u, ref.size()); + UNIT_ASSERT_VALUES_EQUAL(30, ref[2]); + ref[2] = 50; + UNIT_ASSERT_VALUES_EQUAL(50, x[2]); + + TArrayRef constRef(x); + UNIT_ASSERT_VALUES_EQUAL(3u, constRef.size()); + UNIT_ASSERT_VALUES_EQUAL(50, constRef[2]); + ref[0] = 100; + UNIT_ASSERT_VALUES_EQUAL(constRef[0], 100); + } + + Y_UNIT_TEST(TestAccessingElements) { + int a[]{1, 2, 3}; + TArrayRef ref(a); + + UNIT_ASSERT_VALUES_EQUAL(ref[0], 1); + UNIT_ASSERT_VALUES_EQUAL(ref.at(0), 1); + + ref[0] = 5; + UNIT_ASSERT_VALUES_EQUAL(a[0], 5); + + //FIXME: size checks are implemented via Y_ASSERT, hence there is no way to test them + } + + Y_UNIT_TEST(TestFrontBack) { + const int x[] = {1, 2, 3}; + const TArrayRef rx{x}; + UNIT_ASSERT_VALUES_EQUAL(rx.front(), 1); + UNIT_ASSERT_VALUES_EQUAL(rx.back(), 3); + + int y[] = {1, 2, 3}; + TArrayRef ry{y}; + UNIT_ASSERT_VALUES_EQUAL(ry.front(), 1); + UNIT_ASSERT_VALUES_EQUAL(ry.back(), 3); + + ry.front() = 100; + ry.back() = 500; + UNIT_ASSERT_VALUES_EQUAL(ry.front(), 100); + UNIT_ASSERT_VALUES_EQUAL(ry.back(), 500); + UNIT_ASSERT_VALUES_EQUAL(y[0], 100); + UNIT_ASSERT_VALUES_EQUAL(y[2], 500); + } + + Y_UNIT_TEST(TestIterator) { + int array[] = {17, 19, 21}; + TArrayRef r(array, 3); + + TArrayRef::iterator iterator = r.begin(); + for (auto& i : array) { + UNIT_ASSERT(iterator != r.end()); + UNIT_ASSERT_VALUES_EQUAL(i, *iterator); + ++iterator; + } + UNIT_ASSERT(iterator == r.end()); + } + + Y_UNIT_TEST(TestReverseIterators) { + const int x[] = {1, 2, 3}; + const TArrayRef rx{x}; + auto i = rx.crbegin(); + UNIT_ASSERT_VALUES_EQUAL(*i, 3); + ++i; + UNIT_ASSERT_VALUES_EQUAL(*i, 2); + ++i; + UNIT_ASSERT_VALUES_EQUAL(*i, 1); + ++i; + UNIT_ASSERT_EQUAL(i, rx.crend()); + } + + Y_UNIT_TEST(TestConstIterators) { + int x[] = {1, 2, 3}; + TArrayRef rx{x}; + UNIT_ASSERT_EQUAL(rx.begin(), rx.cbegin()); + UNIT_ASSERT_EQUAL(rx.end(), rx.cend()); + UNIT_ASSERT_EQUAL(rx.rbegin(), rx.crbegin()); + UNIT_ASSERT_EQUAL(rx.rend(), rx.crend()); + + int w[] = {1, 2, 3}; + const TArrayRef rw{w}; + UNIT_ASSERT_EQUAL(rw.begin(), rw.cbegin()); + UNIT_ASSERT_EQUAL(rw.end(), rw.cend()); + UNIT_ASSERT_EQUAL(rw.rbegin(), rw.crbegin()); + UNIT_ASSERT_EQUAL(rw.rend(), rw.crend()); + + int y[] = {1, 2, 3}; + TArrayRef ry{y}; + UNIT_ASSERT_EQUAL(ry.begin(), ry.cbegin()); + UNIT_ASSERT_EQUAL(ry.end(), ry.cend()); + UNIT_ASSERT_EQUAL(ry.rbegin(), ry.crbegin()); + UNIT_ASSERT_EQUAL(ry.rend(), ry.crend()); + + const int z[] = {1, 2, 3}; + TArrayRef rz{z}; + UNIT_ASSERT_EQUAL(rz.begin(), rz.cbegin()); + UNIT_ASSERT_EQUAL(rz.end(), rz.cend()); + UNIT_ASSERT_EQUAL(rz.rbegin(), rz.crbegin()); + UNIT_ASSERT_EQUAL(rz.rend(), rz.crend()); + + const int q[] = {1, 2, 3}; + const TArrayRef rq{q}; + UNIT_ASSERT_EQUAL(rq.begin(), rq.cbegin()); + UNIT_ASSERT_EQUAL(rq.end(), rq.cend()); + UNIT_ASSERT_EQUAL(rq.rbegin(), rq.crbegin()); + UNIT_ASSERT_EQUAL(rq.rend(), rq.crend()); + } + + Y_UNIT_TEST(TestCreatingFromStringLiteral) { + TConstArrayRef knownSizeRef("123", 3); + size_t ret = 0; + + for (char ch : knownSizeRef) { + ret += ch - '0'; + } + + UNIT_ASSERT_VALUES_EQUAL(ret, 6); + UNIT_ASSERT_VALUES_EQUAL(knownSizeRef.size(), 3); + UNIT_ASSERT_VALUES_EQUAL(knownSizeRef.at(0), '1'); + + /* + * When TArrayRef is being constructed from string literal, + * trailing zero will be added into it. + */ + TConstArrayRef autoSizeRef("456"); + UNIT_ASSERT_VALUES_EQUAL(autoSizeRef[0], '4'); + UNIT_ASSERT_VALUES_EQUAL(autoSizeRef[3], '\0'); + } + + Y_UNIT_TEST(TestEqualityOperator) { + static constexpr size_t size = 5; + int a[size]{1, 2, 3, 4, 5}; + int b[size]{5, 4, 3, 2, 1}; + int c[size - 1]{5, 4, 3, 2}; + float d[size]{1.f, 2.f, 3.f, 4.f, 5.f}; + + TArrayRef aRef(a); + TConstArrayRef aConstRef(a, size); + + TArrayRef bRef(b); + + TArrayRef cRef(c, size - 1); + + TArrayRef dRef(d, size); + TConstArrayRef dConstRef(d, size); + + UNIT_ASSERT_EQUAL(aRef, aConstRef); + UNIT_ASSERT_EQUAL(dRef, dConstRef); + + UNIT_ASSERT_UNEQUAL(aRef, cRef); + UNIT_ASSERT_UNEQUAL(aRef, bRef); + + TArrayRef bSubRef(b, size - 1); + + //Testing if operator== compares values, not pointers + UNIT_ASSERT_EQUAL(cRef, bSubRef); + } + + Y_UNIT_TEST(TestImplicitConstructionFromContainer) { + /* Just test compilation. */ + auto fc = [](TArrayRef) {}; + auto fm = [](TArrayRef) {}; + + fc(TVector({1})); + + const TVector ac = {1}; + TVector am = {1}; + + fc(ac); + fc(am); + fm(am); + // fm(ac); // This one shouldn't compile. + } + + Y_UNIT_TEST(TestFirstLastSubspan) { + const int arr[] = {1, 2, 3, 4, 5}; + TArrayRef aRef(arr); + + UNIT_ASSERT_EQUAL(aRef.first(2), MakeArrayRef(std::vector{1, 2})); + UNIT_ASSERT_EQUAL(aRef.last(2), MakeArrayRef(std::vector{4, 5})); + UNIT_ASSERT_EQUAL(aRef.subspan(2), MakeArrayRef(std::vector{3, 4, 5})); + UNIT_ASSERT_EQUAL(aRef.subspan(1, 3), MakeArrayRef(std::vector{2, 3, 4})); + } + + Y_UNIT_TEST(TestSlice) { + const int a0[] = {1, 2, 3}; + TArrayRef r0(a0); + TArrayRef s0 = r0.Slice(2); + + UNIT_ASSERT_VALUES_EQUAL(s0.size(), 1); + UNIT_ASSERT_VALUES_EQUAL(s0[0], 3); + + const int a1[] = {1, 2, 3, 4}; + TArrayRef r1(a1); + TArrayRef s1 = r1.Slice(2, 1); + + UNIT_ASSERT_VALUES_EQUAL(s1.size(), 1); + UNIT_ASSERT_VALUES_EQUAL(s1[0], 3); + + //FIXME: size checks are implemented via Y_ASSERT, hence there is no way to test them + } + + Y_UNIT_TEST(SubRegion) { + TVector x; + for (size_t i = 0; i < 42; ++i) { + x.push_back('a' + (i * 42424243) % 13); + } + TArrayRef ref(x.data(), 42); + for (size_t i = 0; i <= 50; ++i) { + TVector expected; + for (size_t j = 0; j <= 100; ++j) { + UNIT_ASSERT(MakeArrayRef(expected) == ref.SubRegion(i, j)); + if (i + j < 42) { + expected.push_back(x[i + j]); + } + } + } + } + + Y_UNIT_TEST(TestAsBytes) { + const int16_t constArr[] = {1, 2, 3}; + TArrayRef constRef(constArr); + auto bytesRef = as_bytes(constRef); + + UNIT_ASSERT_VALUES_EQUAL(bytesRef.size(), sizeof(int16_t) * constRef.size()); + UNIT_ASSERT_EQUAL( + bytesRef, + MakeArrayRef(std::vector{0x01, 0x00, 0x02, 0x00, 0x03, 0x00})); + + //should not compile + //as_writable_bytes(constRef); + } + + Y_UNIT_TEST(TestAsWritableBytes) { + uint32_t uintArr[] = {0x0c'00'0d'0e}; + TArrayRef uintRef(uintArr); + auto writableBytesRef = as_writable_bytes(uintRef); + + UNIT_ASSERT_VALUES_EQUAL(writableBytesRef.size(), sizeof(uint32_t)); + UNIT_ASSERT_EQUAL( + writableBytesRef, + MakeArrayRef(std::vector{0x0e, 0x0d, 0x00, 0x0c})); + + uint32_t newVal = 0xde'ad'be'ef; + std::memcpy(writableBytesRef.data(), &newVal, writableBytesRef.size()); + UNIT_ASSERT_VALUES_EQUAL(uintArr[0], newVal); + } + + Y_UNIT_TEST(TestTypeDeductionViaMakeArrayRef) { + TVector vec{17, 19, 21}; + TArrayRef ref = MakeArrayRef(vec); + UNIT_ASSERT_VALUES_EQUAL(21, ref[2]); + ref[1] = 23; + UNIT_ASSERT_VALUES_EQUAL(23, vec[1]); + + const TVector& constVec(vec); + TArrayRef constRef = MakeArrayRef(constVec); + UNIT_ASSERT_VALUES_EQUAL(21, constRef[2]); + + TArrayRef constRefFromNonConst = MakeArrayRef(vec); + UNIT_ASSERT_VALUES_EQUAL(23, constRefFromNonConst[1]); + } + + static void Do(const TArrayRef a) { + a[0] = 8; + } + + Y_UNIT_TEST(TestConst) { + int a[] = {1, 2}; + Do(a); + UNIT_ASSERT_VALUES_EQUAL(a[0], 8); + } + + Y_UNIT_TEST(TestConstexpr) { + static constexpr const int a[] = {1, 2, -3, -4}; + static constexpr const auto r0 = MakeArrayRef(a, 1); + static_assert(r0.size() == 1, "r0.size() is not equal 1"); + static_assert(r0.data()[0] == 1, "r0.data()[0] is not equal to 1"); + + static constexpr const TArrayRef r1{a}; + static_assert(r1.size() == 4, "r1.size() is not equal to 4"); + static_assert(r1.data()[3] == -4, "r1.data()[3] is not equal to -4"); + + static constexpr const TArrayRef r2 = r1; + static_assert(r2.size() == 4, "r2.size() is not equal to 4"); + static_assert(r2.data()[2] == -3, "r2.data()[2] is not equal to -3"); + } + + template + static void Foo(const TConstArrayRef) { + // noop + } + + Y_UNIT_TEST(TestMakeConstArrayRef) { + TVector data; + + // Won't compile because can't deduce `T` for `Foo` + // Foo(data); + + // Won't compile because again can't deduce `T` for `Foo` + // Foo(MakeArrayRef(data)); + + // Success! + Foo(MakeConstArrayRef(data)); + + const TVector constData; + Foo(MakeConstArrayRef(constData)); + } +} diff --git a/util/generic/array_ref_ut.pyx b/util/generic/array_ref_ut.pyx new file mode 100644 index 00000000000..67b69a365c9 --- /dev/null +++ b/util/generic/array_ref_ut.pyx @@ -0,0 +1,28 @@ +import pytest +import unittest +from util.generic.array_ref cimport TArrayRef +from util.generic.vector cimport TVector + + +class TestArrayRef(unittest.TestCase): + def test_array_data_reference(self): + array_size = 30 + cdef TVector[int] vec + for i in xrange(array_size): + vec.push_back(i) + cdef TArrayRef[int] array_ref = TArrayRef[int](vec.data(), vec.size()) + for i in xrange(array_size / 2): + array_ref[array_size - 1 - i] = array_ref[i] + for i in xrange(array_size): + self.assertEqual(array_ref[i], array_size - 1 - i) + + def test_array_vec_reference(self): + array_size = 30 + cdef TVector[int] vec + for i in xrange(array_size): + vec.push_back(i) + cdef TArrayRef[int] array_ref = TArrayRef[int](vec) + for i in xrange(array_size / 2): + array_ref[array_size - 1 - i] = array_ref[i] + for i in xrange(array_size): + self.assertEqual(array_ref[i], array_size - 1 - i) \ No newline at end of file diff --git a/util/generic/array_size.cpp b/util/generic/array_size.cpp new file mode 100644 index 00000000000..763f96e4ede --- /dev/null +++ b/util/generic/array_size.cpp @@ -0,0 +1 @@ +#include "array_size.h" diff --git a/util/generic/array_size.h b/util/generic/array_size.h new file mode 100644 index 00000000000..4d5f18ce631 --- /dev/null +++ b/util/generic/array_size.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace NArraySizePrivate { + template + struct TArraySize; + + template + struct TArraySize { + enum { + Result = N + }; + }; + + template + struct TArraySize { + enum { + Result = N + }; + }; +} + +#define Y_ARRAY_SIZE(arr) ((size_t)::NArraySizePrivate::TArraySize::Result) diff --git a/util/generic/array_size_ut.cpp b/util/generic/array_size_ut.cpp new file mode 100644 index 00000000000..13f45903c51 --- /dev/null +++ b/util/generic/array_size_ut.cpp @@ -0,0 +1,22 @@ +#include "array_size.h" + +#include + +Y_UNIT_TEST_SUITE(ArraySizeTest) { + Y_UNIT_TEST(Test1) { + int x[100]; + Y_UNUSED(x); /* Make MSVC happy. */ + + UNIT_ASSERT_VALUES_EQUAL(Y_ARRAY_SIZE(x), 100); + } + + Y_UNIT_TEST(Test2) { + struct T { + }; + + T x[1]; + Y_UNUSED(x); /* Make MSVC happy. */ + + UNIT_ASSERT_VALUES_EQUAL(Y_ARRAY_SIZE(x), 1); + } +} diff --git a/util/generic/bitmap.cpp b/util/generic/bitmap.cpp new file mode 100644 index 00000000000..a629a870d0f --- /dev/null +++ b/util/generic/bitmap.cpp @@ -0,0 +1 @@ +#include "bitmap.h" diff --git a/util/generic/bitmap.h b/util/generic/bitmap.h new file mode 100644 index 00000000000..dfac88e4b8d --- /dev/null +++ b/util/generic/bitmap.h @@ -0,0 +1,1115 @@ +#pragma once + +#include "fwd.h" +#include "ptr.h" +#include "bitops.h" +#include "typetraits.h" +#include "algorithm.h" +#include "utility.h" + +#include +#include +#include +#include + +namespace NBitMapPrivate { + // Returns number of bits set; result is in most significatnt byte + inline ui64 ByteSums(ui64 x) { + ui64 byteSums = x - ((x & 0xAAAAAAAAAAAAAAAAULL) >> 1); + + byteSums = (byteSums & 0x3333333333333333ULL) + ((byteSums >> 2) & 0x3333333333333333ULL); + byteSums = (byteSums + (byteSums >> 4)) & 0x0F0F0F0F0F0F0F0FULL; + + return byteSums * 0x0101010101010101ULL; + } + + // better than intrinsics without -mpopcnt + template + static unsigned CountBitsPrivate(T v) noexcept { + return static_cast(ByteSums(v) >> 56); + } + + template + struct TSanitizeMask { + static constexpr TChunkType Value = ~((~TChunkType(0)) << ExtraBits); + }; + + template + struct TSanitizeMask { + static constexpr TChunkType Value = (TChunkType)~TChunkType(0u); + }; + + template + struct TBigToSmallDataCopier { + static_assert(sizeof(TTargetChunk) < sizeof(TSourceChunk), "expect sizeof(TTargetChunk) < sizeof(TSourceChunk)"); + static_assert(0 == sizeof(TSourceChunk) % sizeof(TTargetChunk), "expect 0 == sizeof(TSourceChunk) % sizeof(TTargetChunk)"); + + static constexpr size_t BLOCK_SIZE = sizeof(TSourceChunk) / sizeof(TTargetChunk); + + union TCnv { + TSourceChunk BigData; + TTargetChunk SmallData[BLOCK_SIZE]; + }; + + static inline void CopyChunk(TTargetChunk* target, TSourceChunk source) { + TCnv c; + c.BigData = source; +#if defined(_big_endian_) + ::ReverseCopy(c.SmallData, c.SmallData + Y_ARRAY_SIZE(c.SmallData), target); +#else + ::Copy(c.SmallData, c.SmallData + Y_ARRAY_SIZE(c.SmallData), target); +#endif + } + + static inline void Copy(TTargetChunk* target, size_t targetSize, const TSourceChunk* source, size_t sourceSize) { + Y_ASSERT(targetSize >= sourceSize * BLOCK_SIZE); + if (targetSize > sourceSize * BLOCK_SIZE) { + ::Fill(target + sourceSize * BLOCK_SIZE, target + targetSize, 0); + } + for (size_t i = 0; i < sourceSize; ++i) { + CopyChunk(target + i * BLOCK_SIZE, source[i]); + } + } + }; + + template + struct TSmallToBigDataCopier { + static_assert(sizeof(TTargetChunk) > sizeof(TSourceChunk), "expect sizeof(TTargetChunk) > sizeof(TSourceChunk)"); + static_assert(0 == sizeof(TTargetChunk) % sizeof(TSourceChunk), "expect 0 == sizeof(TTargetChunk) % sizeof(TSourceChunk)"); + + static constexpr size_t BLOCK_SIZE = sizeof(TTargetChunk) / sizeof(TSourceChunk); + + union TCnv { + TSourceChunk SmallData[BLOCK_SIZE]; + TTargetChunk BigData; + }; + + static inline TTargetChunk CopyFullChunk(const TSourceChunk* source) { + TCnv c; +#if defined(_big_endian_) + ::ReverseCopy(source, source + BLOCK_SIZE, c.SmallData); +#else + ::Copy(source, source + BLOCK_SIZE, c.SmallData); +#endif + return c.BigData; + } + + static inline TTargetChunk CopyPartChunk(const TSourceChunk* source, size_t count) { + Y_ASSERT(count <= BLOCK_SIZE); + TCnv c; + c.BigData = 0; +#if defined(_big_endian_) + ::ReverseCopy(source, source + count, c.SmallData); +#else + ::Copy(source, source + count, c.SmallData); +#endif + return c.BigData; + } + + static inline void Copy(TTargetChunk* target, size_t targetSize, const TSourceChunk* source, size_t sourceSize) { + Y_ASSERT(targetSize * BLOCK_SIZE >= sourceSize); + if (targetSize * BLOCK_SIZE > sourceSize) { + ::Fill(target + sourceSize / BLOCK_SIZE, target + targetSize, 0); + } + size_t i = 0; + for (; i < sourceSize / BLOCK_SIZE; ++i) { + target[i] = CopyFullChunk(source + i * BLOCK_SIZE); + } + if (0 != sourceSize % BLOCK_SIZE) { + target[i] = CopyPartChunk(source + i * BLOCK_SIZE, sourceSize % BLOCK_SIZE); + } + } + }; + + template + struct TUniformDataCopier { + static inline void Copy(TChunk* target, size_t targetSize, const TChunk* source, size_t sourceSize) { + Y_ASSERT(targetSize >= sourceSize); + for (size_t i = 0; i < sourceSize; ++i) { + target[i] = source[i]; + } + for (size_t i = sourceSize; i < targetSize; ++i) { + target[i] = 0; + } + } + }; + + template + struct TIsSmaller { + enum { + Result = sizeof(TFirst) < sizeof(TSecond) + }; + }; + + template + struct TDataCopier: public std::conditional_t::value, TUniformDataCopier, std::conditional_t::Result, TBigToSmallDataCopier, TSmallToBigDataCopier>> { + }; + + template + inline void CopyData(TTargetChunk* target, size_t targetSize, const TSourceChunk* source, size_t sourceSize) { + TDataCopier::Copy(target, targetSize, source, sourceSize); + } + + template + struct TFixedStorage { + using TChunk = TChunkType; + + static constexpr size_t Size = (BitCount + 8 * sizeof(TChunk) - 1) / (8 * sizeof(TChunk)); + + TChunk Data[Size]; + + TFixedStorage() { + Zero(Data); + } + + TFixedStorage(const TFixedStorage& st) { + for (size_t i = 0; i < Size; ++i) { + Data[i] = st.Data[i]; + } + } + + template + TFixedStorage(const TOtherChunk* data, size_t size) { + Y_ABORT_UNLESS(Size * sizeof(TChunk) >= size * sizeof(TOtherChunk), "Exceeding bitmap storage capacity"); + CopyData(Data, Size, data, size); + } + + Y_FORCE_INLINE void Swap(TFixedStorage& st) { + for (size_t i = 0; i < Size; ++i) { + DoSwap(Data[i], st.Data[i]); + } + } + + Y_FORCE_INLINE static constexpr size_t GetBitCapacity() noexcept { + return BitCount; + } + + Y_FORCE_INLINE static constexpr size_t GetChunkCapacity() noexcept { + return Size; + } + + // Returns true if the resulting storage capacity is enough to fit the requested size + Y_FORCE_INLINE static constexpr bool ExpandBitSize(const size_t bitSize) noexcept { + return bitSize <= BitCount; + } + + Y_FORCE_INLINE void Sanitize() { + Data[Size - 1] &= TSanitizeMask::Value; + } + }; + + // Dynamically expanded storage. + // It uses "on stack" realization with no allocation for one chunk spaces + template + struct TDynamicStorage { + using TChunk = TChunkType; + + size_t Size; + TChunk StackData; + TArrayHolder ArrayData; + TChunk* Data; + + TDynamicStorage() + : Size(1) + , StackData(0) + , Data(&StackData) + { + } + + TDynamicStorage(const TDynamicStorage& st) + : Size(1) + , StackData(0) + , Data(&StackData) + { + ExpandSize(st.Size, false); + for (size_t i = 0; i < st.Size; ++i) { + Data[i] = st.Data[i]; + } + for (size_t i = st.Size; i < Size; ++i) { + Data[i] = 0; + } + } + + template + TDynamicStorage(const TOtherChunk* data, size_t size) + : Size(1) + , StackData(0) + , Data(&StackData) + { + ExpandBitSize(size * sizeof(TOtherChunk) * 8, false); + CopyData(Data, Size, data, size); + } + + Y_FORCE_INLINE void Swap(TDynamicStorage& st) { + DoSwap(Size, st.Size); + DoSwap(StackData, st.StackData); + DoSwap(ArrayData, st.ArrayData); + Data = 1 == Size ? &StackData : ArrayData.Get(); + st.Data = 1 == st.Size ? &st.StackData : st.ArrayData.Get(); + } + + Y_FORCE_INLINE size_t GetBitCapacity() const { + return Size * 8 * sizeof(TChunk); + } + + Y_FORCE_INLINE size_t GetChunkCapacity() const { + return Size; + } + + // Returns true if the resulting storage capacity is enough to fit the requested size + Y_FORCE_INLINE bool ExpandSize(size_t size, bool keepData = true) { + if (size > Size) { + size = Max(size, Size * 2); + TArrayHolder newData(new TChunk[size]); + if (keepData) { + for (size_t i = 0; i < Size; ++i) { + newData[i] = Data[i]; + } + for (size_t i = Size; i < size; ++i) { + newData[i] = 0; + } + } + DoSwap(ArrayData, newData); + Data = ArrayData.Get(); + Size = size; + } + return true; + } + + Y_FORCE_INLINE bool ExpandBitSize(size_t bitSize, bool keepData = true) { + return ExpandSize((bitSize + 8 * sizeof(TChunk) - 1) / (8 * sizeof(TChunk)), keepData); + } + + Y_FORCE_INLINE void Sanitize() { + } + }; + + template + struct TDivCount { + static constexpr size_t Value = 1 + TDivCount<(num >> 1)>::Value; + }; + + template <> + struct TDivCount<0> { + static constexpr size_t Value = 0; + }; + +} + +template +struct TFixedBitMapTraits { + using TChunk = TChunkType; + using TStorage = NBitMapPrivate::TFixedStorage; +}; + +template +struct TDynamicBitMapTraits { + using TChunk = TChunkType; + using TStorage = NBitMapPrivate::TDynamicStorage; +}; + +template +class TBitMapOps { +public: + using TChunk = typename TTraits::TChunk; + using TThis = TBitMapOps; + +private: + static_assert(std::is_unsigned::value, "expect std::is_unsigned::value"); + + static constexpr size_t BitsPerChunk = 8 * sizeof(TChunk); + static constexpr TChunk ModMask = static_cast(BitsPerChunk - 1); + static constexpr size_t DivCount = NBitMapPrivate::TDivCount::Value - 1; + static constexpr TChunk FullChunk = (TChunk)~TChunk(0); + + template + friend class TBitMapOps; + + using TStorage = typename TTraits::TStorage; + + // The smallest unsigned type, which can be used in bit ops + using TIntType = std::conditional_t; + + TStorage Mask; + +public: + class TReference { + private: + friend class TBitMapOps; + + TChunk* Chunk; + size_t Offset; + + TReference(TChunk* c, size_t offset) + : Chunk(c) + , Offset(offset) + { + } + + public: + ~TReference() = default; + + Y_FORCE_INLINE TReference& operator=(bool val) { + if (val) + *Chunk |= static_cast(1) << Offset; + else + *Chunk &= ~(static_cast(1) << Offset); + + return *this; + } + + Y_FORCE_INLINE TReference& operator=(const TReference& ref) { + if (ref) + *Chunk |= static_cast(1) << Offset; + else + *Chunk &= ~(static_cast(1) << Offset); + + return *this; + } + + Y_FORCE_INLINE bool operator~() const { + return 0 == (*Chunk & (static_cast(1) << Offset)); + } + + Y_FORCE_INLINE operator bool() const { + return 0 != (*Chunk & (static_cast(1) << Offset)); + } + + Y_FORCE_INLINE TReference& Flip() { + *Chunk ^= static_cast(1) << Offset; + return *this; + } + }; + +private: + struct TSetOp { + static constexpr TChunk Op(const TChunk src, const TChunk mask) noexcept { + return src | mask; + } + }; + + struct TResetOp { + static constexpr TChunk Op(const TChunk src, const TChunk mask) noexcept { + return src & ~mask; + } + }; + + template + void UpdateRange(size_t start, size_t end) { + const size_t startChunk = start >> DivCount; + const size_t startBitOffset = start & ModMask; + + const size_t endChunk = end >> DivCount; + const size_t endBitOffset = end & ModMask; + + size_t bitOffset = startBitOffset; + for (size_t chunk = startChunk; chunk <= endChunk; ++chunk) { + TChunk updateMask = FullChunk << bitOffset; + if (chunk == endChunk) { + updateMask ^= FullChunk << endBitOffset; + if (!updateMask) + break; + } + Mask.Data[chunk] = TUpdateOp::Op(Mask.Data[chunk], updateMask); + bitOffset = 0; + } + } + +public: + TBitMapOps() = default; + + TBitMapOps(TChunk val) { + Mask.Data[0] = val; + Mask.Sanitize(); + } + + TBitMapOps(const TThis&) = default; + + template + TBitMapOps(const TBitMapOps& bitmap) + : Mask(bitmap.Mask.Data, bitmap.Mask.GetChunkCapacity()) + { + Mask.Sanitize(); + } + + template + Y_FORCE_INLINE bool operator==(const TBitMapOps& bitmap) const { + return Equal(bitmap); + } + + Y_FORCE_INLINE TThis& operator=(const TThis& bitmap) { + if (this != &bitmap) { + TThis bm(bitmap); + Swap(bm); + } + return *this; + } + + template + Y_FORCE_INLINE TThis& operator=(const TBitMapOps& bitmap) { + TThis bm(bitmap); + Swap(bm); + return *this; + } + + template + Y_FORCE_INLINE TThis& operator&=(const TBitMapOps& bitmap) { + return And(bitmap); + } + + Y_FORCE_INLINE TThis& operator&=(const TChunk& val) { + return And(val); + } + + template + Y_FORCE_INLINE TThis& operator|=(const TBitMapOps& bitmap) { + return Or(bitmap); + } + + Y_FORCE_INLINE TThis& operator|=(const TChunk& val) { + return Or(val); + } + + template + Y_FORCE_INLINE TThis& operator^=(const TBitMapOps& bitmap) { + return Xor(bitmap); + } + + Y_FORCE_INLINE TThis& operator^=(const TChunk& val) { + return Xor(val); + } + + template + Y_FORCE_INLINE TThis& operator-=(const TBitMapOps& bitmap) { + return SetDifference(bitmap); + } + + Y_FORCE_INLINE TThis& operator-=(const TChunk& val) { + return SetDifference(val); + } + + Y_FORCE_INLINE TThis& operator<<=(size_t pos) { + return LShift(pos); + } + + Y_FORCE_INLINE TThis& operator>>=(size_t pos) { + return RShift(pos); + } + + Y_FORCE_INLINE TThis operator<<(size_t pos) const { + return TThis(*this).LShift(pos); + } + + Y_FORCE_INLINE TThis operator>>(size_t pos) const { + return TThis(*this).RShift(pos); + } + + Y_FORCE_INLINE bool operator[](size_t pos) const { + return Get(pos); + } + + Y_FORCE_INLINE TReference operator[](size_t pos) { + const bool fitStorage = Mask.ExpandBitSize(pos + 1); + Y_ASSERT(fitStorage); + return TReference(&Mask.Data[pos >> DivCount], ModMask & pos); + } + + Y_FORCE_INLINE void Swap(TThis& bitmap) { + DoSwap(Mask, bitmap.Mask); + } + + Y_FORCE_INLINE TThis& Set(size_t pos) { + const bool fitStorage = Mask.ExpandBitSize(pos + 1); + Y_ASSERT(fitStorage); + Mask.Data[pos >> DivCount] |= static_cast(1) << (pos & ModMask); + return *this; + } + + // Fills the specified [start, end) bit range by the 1. Other bits are kept unchanged + TThis& Set(size_t start, size_t end) { + Y_ASSERT(start <= end); + if (start < end) { + Reserve(end); + UpdateRange(start, end); + } + return *this; + } + + Y_FORCE_INLINE TThis& Reset(size_t pos) { + if ((pos >> DivCount) < Mask.GetChunkCapacity()) { + Mask.Data[pos >> DivCount] &= ~(static_cast(1) << (pos & ModMask)); + } + return *this; + } + + // Clears the specified [start, end) bit range. Other bits are kept unchanged + TThis& Reset(size_t start, size_t end) { + Y_ASSERT(start <= end); + if (start < end && (start >> DivCount) < Mask.GetChunkCapacity()) { + UpdateRange(start, Min(end, Mask.GetBitCapacity())); + } + return *this; + } + + Y_FORCE_INLINE TThis& Flip(size_t pos) { + const bool fitStorage = Mask.ExpandBitSize(pos + 1); + Y_ASSERT(fitStorage); + Mask.Data[pos >> DivCount] ^= static_cast(1) << (pos & ModMask); + return *this; + } + + Y_FORCE_INLINE bool Get(size_t pos) const { + if ((pos >> DivCount) < Mask.GetChunkCapacity()) { + return Mask.Data[pos >> DivCount] & (static_cast(1) << (pos & ModMask)); + } + return false; + } + + template + void Export(size_t pos, TTo& to) const { + static_assert(std::is_unsigned::value, "expect std::is_unsigned::value"); + to = 0; + size_t chunkpos = pos >> DivCount; + if (chunkpos >= Mask.GetChunkCapacity()) + return; + if ((pos & ModMask) == 0) { + if (sizeof(TChunk) >= sizeof(TTo)) + to = (TTo)Mask.Data[chunkpos]; + else //if (sizeof(TChunk) < sizeof(TTo)) + NBitMapPrivate::CopyData(&to, 1, Mask.Data + chunkpos, Min(((sizeof(TTo) * 8) >> DivCount), Mask.GetChunkCapacity() - chunkpos)); + } else if ((pos & (sizeof(TTo) * 8 - 1)) == 0 && sizeof(TChunk) >= 2 * sizeof(TTo)) + to = (TTo)(Mask.Data[chunkpos] >> (pos & ModMask)); + else { + static constexpr size_t copyToSize = (sizeof(TChunk) >= sizeof(TTo)) ? (sizeof(TChunk) / sizeof(TTo)) + 2 : 3; + TTo temp[copyToSize] = {0, 0}; + //or use non defined by now TBitmap::CopyData,RShift(pos & ModMask),Export(0,to) + NBitMapPrivate::CopyData(temp, copyToSize, Mask.Data + chunkpos, Min((sizeof(TTo) / sizeof(TChunk)) + 1, Mask.GetChunkCapacity() - chunkpos)); + to = (temp[0] >> (pos & ModMask)) | (temp[1] << (8 * sizeof(TTo) - (pos & ModMask))); + } + } + + Y_FORCE_INLINE bool Test(size_t n) const { + return Get(n); + } + + Y_FORCE_INLINE TThis& Push(bool val) { + LShift(1); + return val ? Set(0) : *this; + } + + Y_FORCE_INLINE bool Pop() { + bool val = Get(0); + return RShift(1), val; + } + + // Clear entire bitmap. Current capacity is kept unchanged + Y_FORCE_INLINE TThis& Clear() { + for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i) { + Mask.Data[i] = 0; + } + return *this; + } + + // Returns bits capacity + Y_FORCE_INLINE constexpr size_t Size() const noexcept { + return Mask.GetBitCapacity(); + } + + Y_FORCE_INLINE void Reserve(size_t bitCount) { + Y_ABORT_UNLESS(Mask.ExpandBitSize(bitCount), "Exceeding bitmap storage capacity"); + } + + Y_FORCE_INLINE size_t ValueBitCount() const { + size_t nonZeroChunk = Mask.GetChunkCapacity() - 1; + while (nonZeroChunk != 0 && !Mask.Data[nonZeroChunk]) + --nonZeroChunk; + return nonZeroChunk || Mask.Data[nonZeroChunk] + ? nonZeroChunk * BitsPerChunk + GetValueBitCount(TIntType(Mask.Data[nonZeroChunk])) + : 0; + } + + Y_PURE_FUNCTION Y_FORCE_INLINE bool Empty() const { + for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i) + if (Mask.Data[i]) + return false; + return true; + } + + bool HasAny(const TThis& bitmap) const { + for (size_t i = 0; i < Min(Mask.GetChunkCapacity(), bitmap.Mask.GetChunkCapacity()); ++i) { + if (0 != (Mask.Data[i] & bitmap.Mask.Data[i])) { + return true; + } + } + return false; + } + + template + Y_FORCE_INLINE bool HasAny(const TBitMapOps& bitmap) const { + return HasAny(TThis(bitmap)); + } + + Y_FORCE_INLINE bool HasAny(const TChunk& val) const { + return 0 != (Mask.Data[0] & val); + } + + bool HasAll(const TThis& bitmap) const { + for (size_t i = 0; i < Min(Mask.GetChunkCapacity(), bitmap.Mask.GetChunkCapacity()); ++i) { + if (bitmap.Mask.Data[i] != (Mask.Data[i] & bitmap.Mask.Data[i])) { + return false; + } + } + for (size_t i = Mask.GetChunkCapacity(); i < bitmap.Mask.GetChunkCapacity(); ++i) { + if (bitmap.Mask.Data[i] != 0) { + return false; + } + } + return true; + } + + template + Y_FORCE_INLINE bool HasAll(const TBitMapOps& bitmap) const { + return HasAll(TThis(bitmap)); + } + + Y_FORCE_INLINE bool HasAll(const TChunk& val) const { + return (Mask.Data[0] & val) == val; + } + + TThis& And(const TThis& bitmap) { + // Don't expand capacity here, because resulting bits in positions, + // which are greater then size of one of these bitmaps, will be zero + for (size_t i = 0; i < Min(bitmap.Mask.GetChunkCapacity(), Mask.GetChunkCapacity()); ++i) + Mask.Data[i] &= bitmap.Mask.Data[i]; + // Clear bits if current bitmap size is greater than AND-ed one + for (size_t i = bitmap.Mask.GetChunkCapacity(); i < Mask.GetChunkCapacity(); ++i) + Mask.Data[i] = 0; + return *this; + } + + template + Y_FORCE_INLINE TThis& And(const TBitMapOps& bitmap) { + return And(TThis(bitmap)); + } + + Y_FORCE_INLINE TThis& And(const TChunk& val) { + Mask.Data[0] &= val; + for (size_t i = 1; i < Mask.GetChunkCapacity(); ++i) + Mask.Data[i] = 0; + return *this; + } + + TThis& Or(const TThis& bitmap) { + const size_t valueBitCount = bitmap.ValueBitCount(); + if (valueBitCount) { + // Memory optimization: expand size only for non-zero bits + Reserve(valueBitCount); + for (size_t i = 0; i < Min(bitmap.Mask.GetChunkCapacity(), Mask.GetChunkCapacity()); ++i) + Mask.Data[i] |= bitmap.Mask.Data[i]; + } + return *this; + } + + template + Y_FORCE_INLINE TThis& Or(const TBitMapOps& bitmap) { + return Or(TThis(bitmap)); + } + + Y_FORCE_INLINE TThis& Or(const TChunk& val) { + Mask.Data[0] |= val; + Mask.Sanitize(); + return *this; + } + + TThis& Xor(const TThis& bitmap) { + Reserve(bitmap.Size()); + for (size_t i = 0; i < bitmap.Mask.GetChunkCapacity(); ++i) + Mask.Data[i] ^= bitmap.Mask.Data[i]; + return *this; + } + + template + Y_FORCE_INLINE TThis& Xor(const TBitMapOps& bitmap) { + return Xor(TThis(bitmap)); + } + + Y_FORCE_INLINE TThis& Xor(const TChunk& val) { + Mask.Data[0] ^= val; + Mask.Sanitize(); + return *this; + } + + TThis& SetDifference(const TThis& bitmap) { + for (size_t i = 0; i < Min(bitmap.Mask.GetChunkCapacity(), Mask.GetChunkCapacity()); ++i) + Mask.Data[i] &= ~bitmap.Mask.Data[i]; + return *this; + } + + template + Y_FORCE_INLINE TThis& SetDifference(const TBitMapOps& bitmap) { + return SetDifference(TThis(bitmap)); + } + + Y_FORCE_INLINE TThis& SetDifference(const TChunk& val) { + Mask.Data[0] &= ~val; + return *this; + } + + Y_FORCE_INLINE TThis& Flip() { + for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i) + Mask.Data[i] = ~Mask.Data[i]; + Mask.Sanitize(); + return *this; + } + + TThis& LShift(size_t shift) { + if (shift != 0) { + const size_t valueBitCount = ValueBitCount(); + // Do nothing for empty bitmap + if (valueBitCount != 0) { + const size_t eshift = shift / BitsPerChunk; + const size_t offset = shift % BitsPerChunk; + const size_t subOffset = BitsPerChunk - offset; + + // Don't verify expand result, so l-shift of fixed bitmap will work in the same way as for unsigned integer. + Mask.ExpandBitSize(valueBitCount + shift); + + if (offset == 0) { + for (size_t i = Mask.GetChunkCapacity() - 1; i >= eshift; --i) { + Mask.Data[i] = Mask.Data[i - eshift]; + } + } else { + for (size_t i = Mask.GetChunkCapacity() - 1; i > eshift; --i) + Mask.Data[i] = (Mask.Data[i - eshift] << offset) | (Mask.Data[i - eshift - 1] >> subOffset); + if (eshift < Mask.GetChunkCapacity()) + Mask.Data[eshift] = Mask.Data[0] << offset; + } + for (size_t i = 0; i < Min(eshift, Mask.GetChunkCapacity()); ++i) + Mask.Data[i] = 0; + + // Cleanup extra high bits in the storage + Mask.Sanitize(); + } + } + return *this; + } + + TThis& RShift(size_t shift) { + if (shift != 0) { + const size_t eshift = shift / BitsPerChunk; + const size_t offset = shift % BitsPerChunk; + if (eshift >= Mask.GetChunkCapacity()) { + Clear(); + + } else { + const size_t limit = Mask.GetChunkCapacity() - eshift - 1; + + if (offset == 0) { + for (size_t i = 0; i <= limit; ++i) { + Mask.Data[i] = Mask.Data[i + eshift]; + } + } else { + const size_t subOffset = BitsPerChunk - offset; + for (size_t i = 0; i < limit; ++i) + Mask.Data[i] = (Mask.Data[i + eshift] >> offset) | (Mask.Data[i + eshift + 1] << subOffset); + Mask.Data[limit] = Mask.Data[Mask.GetChunkCapacity() - 1] >> offset; + } + + for (size_t i = limit + 1; i < Mask.GetChunkCapacity(); ++i) + Mask.Data[i] = 0; + } + } + return *this; + } + + // Applies bitmap at the specified offset using OR operator. + // This method is optimized combination of Or() and LShift(), which allows reducing memory allocation + // when combining long dynamic bitmaps. + TThis& Or(const TThis& bitmap, size_t offset) { + if (0 == offset) + return Or(bitmap); + + const size_t otherValueBitCount = bitmap.ValueBitCount(); + // Continue only if OR-ed bitmap have non-zero bits + if (otherValueBitCount) { + const size_t chunkShift = offset / BitsPerChunk; + const size_t subShift = offset % BitsPerChunk; + const size_t subOffset = BitsPerChunk - subShift; + + Reserve(otherValueBitCount + offset); + + if (subShift == 0) { + for (size_t i = chunkShift; i < Min(bitmap.Mask.GetChunkCapacity() + chunkShift, Mask.GetChunkCapacity()); ++i) { + Mask.Data[i] |= bitmap.Mask.Data[i - chunkShift]; + } + } else { + Mask.Data[chunkShift] |= bitmap.Mask.Data[0] << subShift; + size_t i = chunkShift + 1; + for (; i < Min(bitmap.Mask.GetChunkCapacity() + chunkShift, Mask.GetChunkCapacity()); ++i) { + Mask.Data[i] |= (bitmap.Mask.Data[i - chunkShift] << subShift) | (bitmap.Mask.Data[i - chunkShift - 1] >> subOffset); + } + if (i < Mask.GetChunkCapacity()) + Mask.Data[i] |= bitmap.Mask.Data[i - chunkShift - 1] >> subOffset; + } + } + + return *this; + } + + bool Equal(const TThis& bitmap) const { + if (Mask.GetChunkCapacity() > bitmap.Mask.GetChunkCapacity()) { + for (size_t i = bitmap.Mask.GetChunkCapacity(); i < Mask.GetChunkCapacity(); ++i) { + if (0 != Mask.Data[i]) + return false; + } + } else if (Mask.GetChunkCapacity() < bitmap.Mask.GetChunkCapacity()) { + for (size_t i = Mask.GetChunkCapacity(); i < bitmap.Mask.GetChunkCapacity(); ++i) { + if (0 != bitmap.Mask.Data[i]) + return false; + } + } + size_t size = Min(Mask.GetChunkCapacity(), bitmap.Mask.GetChunkCapacity()); + for (size_t i = 0; i < size; ++i) { + if (Mask.Data[i] != bitmap.Mask.Data[i]) + return false; + } + return true; + } + + template + Y_FORCE_INLINE bool Equal(const TBitMapOps& bitmap) const { + return Equal(TThis(bitmap)); + } + + int Compare(const TThis& bitmap) const { + size_t size = Min(Mask.GetChunkCapacity(), bitmap.Mask.GetChunkCapacity()); + int res = ::memcmp(Mask.Data, bitmap.Mask.Data, size * sizeof(TChunk)); + if (0 != res || Mask.GetChunkCapacity() == bitmap.Mask.GetChunkCapacity()) + return res; + + if (Mask.GetChunkCapacity() > bitmap.Mask.GetChunkCapacity()) { + for (size_t i = bitmap.Mask.GetChunkCapacity(); i < Mask.GetChunkCapacity(); ++i) { + if (0 != Mask.Data[i]) + return 1; + } + } else { + for (size_t i = Mask.GetChunkCapacity(); i < bitmap.Mask.GetChunkCapacity(); ++i) { + if (0 != bitmap.Mask.Data[i]) + return -1; + } + } + return 0; + } + + template + Y_FORCE_INLINE int Compare(const TBitMapOps& bitmap) const { + return Compare(TThis(bitmap)); + } + + // For backward compatibility + Y_FORCE_INLINE static int Compare(const TThis& l, const TThis& r) { + return l.Compare(r); + } + + size_t FirstNonZeroBit() const { + for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i) { + if (Mask.Data[i]) { + // CountTrailingZeroBits() expects unsigned types not smaller than unsigned int. So, convert before calling + return BitsPerChunk * i + CountTrailingZeroBits(TIntType(Mask.Data[i])); + } + } + return Size(); + } + + // Returns position of the next non-zero bit, which offset is greater than specified pos + // Typical loop for iterating bits: + // for (size_t pos = bits.FirstNonZeroBit(); pos != bits.Size(); pos = bits.NextNonZeroBit(pos)) { + // ... + // } + // See Y_FOR_EACH_BIT macro definition at the bottom + size_t NextNonZeroBit(size_t pos) const { + size_t i = (pos + 1) >> DivCount; + if (i < Mask.GetChunkCapacity()) { + const size_t offset = (pos + 1) & ModMask; + // Process the current chunk + if (offset) { + // Zero already iterated trailing bits using mask + const TChunk val = Mask.Data[i] & ((~TChunk(0)) << offset); + if (val) { + return BitsPerChunk * i + CountTrailingZeroBits(TIntType(val)); + } + // Continue with other chunks + ++i; + } + + for (; i < Mask.GetChunkCapacity(); ++i) { + if (Mask.Data[i]) { + return BitsPerChunk * i + CountTrailingZeroBits(TIntType(Mask.Data[i])); + } + } + } + return Size(); + } + + Y_FORCE_INLINE size_t Count() const { + size_t count = 0; + for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i) + count += ::NBitMapPrivate::CountBitsPrivate(Mask.Data[i]); + return count; + } + + void Save(IOutputStream* out) const { + ::Save(out, ui8(sizeof(TChunk))); + ::Save(out, ui64(Size())); + ::SavePodArray(out, Mask.Data, Mask.GetChunkCapacity()); + } + + void Load(IInputStream* inp) { + ui8 chunkSize = 0; + ::Load(inp, chunkSize); + Y_ABORT_UNLESS(size_t(chunkSize) == sizeof(TChunk), "Chunk size is not the same"); + + ui64 bitCount64 = 0; + ::Load(inp, bitCount64); + size_t bitCount = size_t(bitCount64); + Reserve(bitCount); + + size_t chunkCount = 0; + if (bitCount > 0) { + chunkCount = ((bitCount - 1) >> DivCount) + 1; + ::LoadPodArray(inp, Mask.Data, chunkCount); + } + + if (chunkCount < Mask.GetChunkCapacity()) { + ::memset(Mask.Data + chunkCount, 0, (Mask.GetChunkCapacity() - chunkCount) * sizeof(TChunk)); + } + Mask.Sanitize(); + } + + inline size_t Hash() const { + THash chunkHasher; + + size_t hash = chunkHasher(0); + bool tailSkipped = false; + for (size_t i = Mask.GetChunkCapacity(); i > 0; --i) { + if (tailSkipped || Mask.Data[i - 1]) { + hash = ::CombineHashes(hash, chunkHasher(Mask.Data[i - 1])); + tailSkipped = true; + } + } + + return hash; + } + + inline const TChunk* GetChunks() const { + return Mask.Data; + } + + constexpr size_t GetChunkCount() const noexcept { + return Mask.GetChunkCapacity(); + } +}; + +template +inline TBitMapOps operator&(const TBitMapOps& x, const TBitMapOps& y) { + return TBitMapOps(x).And(y); +} + +template +inline TBitMapOps operator&(const TBitMapOps& x, const typename TBitMapOps::TChunk& y) { + return TBitMapOps(x).And(y); +} + +template +inline TBitMapOps operator&(const typename TBitMapOps::TChunk& x, const TBitMapOps& y) { + return TBitMapOps(x).And(y); +} + +template +inline TBitMapOps operator|(const TBitMapOps& x, const TBitMapOps& y) { + return TBitMapOps(x).Or(y); +} + +template +inline TBitMapOps operator|(const TBitMapOps& x, const typename TBitMapOps::TChunk& y) { + return TBitMapOps(x).Or(y); +} + +template +inline TBitMapOps operator|(const typename TBitMapOps::TChunk& x, const TBitMapOps& y) { + return TBitMapOps(x).Or(y); +} + +template +inline TBitMapOps operator^(const TBitMapOps& x, const TBitMapOps& y) { + return TBitMapOps(x).Xor(y); +} + +template +inline TBitMapOps operator^(const TBitMapOps& x, const typename TBitMapOps::TChunk& y) { + return TBitMapOps(x).Xor(y); +} + +template +inline TBitMapOps operator^(const typename TBitMapOps::TChunk& x, const TBitMapOps& y) { + return TBitMapOps(x).Xor(y); +} + +template +inline TBitMapOps operator-(const TBitMapOps& x, const TBitMapOps& y) { + return TBitMapOps(x).SetDifference(y); +} + +template +inline TBitMapOps operator-(const TBitMapOps& x, const typename TBitMapOps::TChunk& y) { + return TBitMapOps(x).SetDifference(y); +} + +template +inline TBitMapOps operator-(const typename TBitMapOps::TChunk& x, const TBitMapOps& y) { + return TBitMapOps(x).SetDifference(y); +} + +template +inline TBitMapOps operator~(const TBitMapOps& x) { + return TBitMapOps(x).Flip(); +} + +/////////////////// Specialization /////////////////////////// + +template +class TBitMap: public TBitMapOps> { +private: + using TBase = TBitMapOps>; + +public: + TBitMap() + : TBase() + { + } + + TBitMap(typename TBase::TChunk val) + : TBase(val) + { + } + + TBitMap(const TBitMap&) = default; + TBitMap& operator=(const TBitMap&) = default; + + template + TBitMap(const TBitMapOps& bitmap) + : TBase(bitmap) + { + } +}; + +using TDynBitMap = TBitMapOps>; + +#define Y_FOR_EACH_BIT(var, bitmap) for (size_t var = (bitmap).FirstNonZeroBit(); var != (bitmap).Size(); var = (bitmap).NextNonZeroBit(var)) + +template +struct THash> { + size_t operator()(const TBitMapOps& elem) const { + return elem.Hash(); + } +}; diff --git a/util/generic/bitmap_ut.cpp b/util/generic/bitmap_ut.cpp new file mode 100644 index 00000000000..087d34a8dcc --- /dev/null +++ b/util/generic/bitmap_ut.cpp @@ -0,0 +1,597 @@ +#include "bitmap.h" + +#include + +#define INIT_BITMAP(bitmap, bits) \ + for (size_t i = 0; i < sizeof(bits) / sizeof(size_t); ++i) { \ + bitmap.Set(bits[i]); \ + } + +#define CHECK_BITMAP(bitmap, bits) \ + { \ + size_t cur = 0, end = sizeof(bits) / sizeof(size_t); \ + for (size_t i = 0; i < bitmap.Size(); ++i) { \ + if (cur < end && bits[cur] == i) { \ + UNIT_ASSERT_EQUAL_C(bitmap.Get(i), true, "pos=" << i); \ + ++cur; \ + } else { \ + UNIT_ASSERT_EQUAL_C(bitmap.Get(i), false, "pos=" << i); \ + } \ + } \ + } + +#define CHECK_BITMAP_WITH_TAIL(bitmap, bits) \ + { \ + size_t cur = 0, end = sizeof(bits) / sizeof(size_t); \ + for (size_t i = 0; i < bitmap.Size(); ++i) { \ + if (cur < end) { \ + if (bits[cur] == i) { \ + UNIT_ASSERT_EQUAL_C(bitmap.Get(i), true, "pos=" << i); \ + ++cur; \ + } else { \ + UNIT_ASSERT_EQUAL_C(bitmap.Get(i), false, "pos=" << i); \ + } \ + } else { \ + UNIT_ASSERT_EQUAL_C(bitmap.Get(i), true, "pos=" << i); \ + } \ + } \ + } + +Y_UNIT_TEST_SUITE(TBitMapTest) { + Y_UNIT_TEST(TestBitMap) { + TBitMap<101> bitmap; + + UNIT_ASSERT_EQUAL(bitmap.Size(), 101); + UNIT_ASSERT_EQUAL(bitmap.Count(), 0); + UNIT_ASSERT_EQUAL(bitmap.FirstNonZeroBit(), 101); + + size_t initBits[] = {0, 50, 100, 45}; + INIT_BITMAP(bitmap, initBits); + bitmap.Reset(45); + + UNIT_ASSERT_EQUAL(bitmap.FirstNonZeroBit(), 0); + size_t setBits[] = {0, 50, 100}; + CHECK_BITMAP(bitmap, setBits); + + for (size_t i = 0; i < bitmap.Size(); ++i) { + UNIT_ASSERT_EQUAL(bitmap.Get(i), bitmap.Test(i)); + } + + UNIT_ASSERT_EQUAL(bitmap.Count(), 3); + + bitmap.Reset(0); + + UNIT_ASSERT_EQUAL(bitmap.FirstNonZeroBit(), 50); + + bitmap.Clear(); + + UNIT_ASSERT_EQUAL(bitmap.Count(), 0); + UNIT_ASSERT_EQUAL(bitmap.Empty(), true); + } + + Y_UNIT_TEST(TestDynBitMap) { + TDynBitMap bitmap; + + UNIT_ASSERT_EQUAL(bitmap.Size(), 64); // Initial capacity + UNIT_ASSERT_EQUAL(bitmap.Count(), 0); + UNIT_ASSERT_EQUAL(bitmap.FirstNonZeroBit(), 64); + + size_t initBits[] = {0, 50, 100, 45}; + INIT_BITMAP(bitmap, initBits); + bitmap.Reset(45); + + UNIT_ASSERT_EQUAL(bitmap.Size(), 128); + + UNIT_ASSERT_EQUAL(bitmap.FirstNonZeroBit(), 0); + size_t setBits[] = {0, 50, 100}; + CHECK_BITMAP(bitmap, setBits); + + for (size_t i = 0; i < bitmap.Size(); ++i) { + UNIT_ASSERT_EQUAL(bitmap.Get(i), bitmap.Test(i)); + } + + UNIT_ASSERT_EQUAL(bitmap.Count(), 3); + + bitmap.Reset(0); + + UNIT_ASSERT_EQUAL(bitmap.FirstNonZeroBit(), 50); + + bitmap.Clear(); + + UNIT_ASSERT_EQUAL(bitmap.Count(), 0); + UNIT_ASSERT_EQUAL(bitmap.Empty(), true); + } + + template + void TestAndImpl() { + TBitMapImpl bitmap1; + TBitMapImpl bitmap2; + + size_t initBits1[] = {10, 20, 50, 100}; + size_t initBits2[] = {10, 11, 22, 50}; + + INIT_BITMAP(bitmap1, initBits1); + INIT_BITMAP(bitmap2, initBits2); + + bitmap1.And(bitmap2); + + UNIT_ASSERT_EQUAL(bitmap1.Count(), 2); + UNIT_ASSERT_EQUAL(bitmap2.Count(), 4); + + size_t resBits[] = {10, 50}; + + CHECK_BITMAP(bitmap1, resBits); + CHECK_BITMAP(bitmap2, initBits2); + } + + Y_UNIT_TEST(TestAndFixed) { + TestAndImpl>(); + } + + Y_UNIT_TEST(TestAndDyn) { + TestAndImpl(); + } + + template + void TestOrImpl() { + TBitMapImpl bitmap1; + TBitMapImpl bitmap2; + + size_t initBits1[] = {0, 10, 11, 76}; + size_t initBits2[] = {1, 11, 22, 50}; + + INIT_BITMAP(bitmap1, initBits1); + INIT_BITMAP(bitmap2, initBits2); + + bitmap1.Or(bitmap2); + + UNIT_ASSERT_EQUAL(bitmap1.Count(), 7); + UNIT_ASSERT_EQUAL(bitmap2.Count(), 4); + + size_t resBits[] = {0, 1, 10, 11, 22, 50, 76}; + + CHECK_BITMAP(bitmap1, resBits); + CHECK_BITMAP(bitmap2, initBits2); + + bitmap1.Clear(); + INIT_BITMAP(bitmap1, initBits1); + + UNIT_ASSERT_EQUAL(bitmap1 | (bitmap2 << 3), TBitMapImpl(bitmap1).Or(bitmap2, 3)); + UNIT_ASSERT_EQUAL(bitmap1 | (bitmap2 << 64), TBitMapImpl(bitmap1).Or(bitmap2, 64)); + UNIT_ASSERT_EQUAL(bitmap1 | (bitmap2 << 66), TBitMapImpl(bitmap1).Or(bitmap2, 66)); + + UNIT_ASSERT_EQUAL(bitmap2 | (bitmap1 << 3), TBitMapImpl(bitmap2).Or(bitmap1, 3)); + UNIT_ASSERT_EQUAL(bitmap2 | (bitmap1 << 64), TBitMapImpl(bitmap2).Or(bitmap1, 64)); + UNIT_ASSERT_EQUAL(bitmap2 | (bitmap1 << 66), TBitMapImpl(bitmap2).Or(bitmap1, 66)); + } + + Y_UNIT_TEST(TestOrFixed) { + TestOrImpl>(); + } + + Y_UNIT_TEST(TestOrDyn) { + TestOrImpl(); + } + + Y_UNIT_TEST(TestCopy) { + TBitMap<101> bitmap1; + size_t initBits[] = {0, 10, 11, 76, 100}; + + INIT_BITMAP(bitmap1, initBits); + + TDynBitMap bitmap2(bitmap1); + CHECK_BITMAP(bitmap2, initBits); + + TBitMap<101> bitmap3(bitmap1); + CHECK_BITMAP(bitmap3, initBits); + + TBitMap<127> bitmap4(bitmap1); + CHECK_BITMAP(bitmap4, initBits); + + TDynBitMap bitmap5; + bitmap5 = bitmap1; + CHECK_BITMAP(bitmap5, initBits); + + TBitMap<101> bitmap6; + bitmap6 = bitmap1; + CHECK_BITMAP(bitmap6, initBits); + + TBitMap<127> bitmap7; + bitmap7 = bitmap1; + CHECK_BITMAP(bitmap7, initBits); + + TBitMap<101> bitmap8; + DoSwap(bitmap8, bitmap6); + CHECK_BITMAP(bitmap8, initBits); + UNIT_ASSERT_EQUAL(bitmap6.Empty(), true); + + TDynBitMap bitmap9; + DoSwap(bitmap9, bitmap5); + CHECK_BITMAP(bitmap9, initBits); + UNIT_ASSERT_EQUAL(bitmap5.Empty(), true); + + // 64->32 + TBitMap<160, ui32> bitmap10(bitmap1); + CHECK_BITMAP(bitmap10, initBits); + + // 64->16 + TBitMap<160, ui16> bitmap11(bitmap1); + CHECK_BITMAP(bitmap11, initBits); + + // 64->8 + TBitMap<160, ui8> bitmap12(bitmap1); + CHECK_BITMAP(bitmap12, initBits); + + // 32->16 + TBitMap<160, ui16> bitmap13(bitmap10); + CHECK_BITMAP(bitmap13, initBits); + + // 32->64 + TBitMap<160, ui64> bitmap14(bitmap10); + CHECK_BITMAP(bitmap14, initBits); + + // 16->64 + TBitMap<160, ui64> bitmap15(bitmap11); + CHECK_BITMAP(bitmap15, initBits); + + // 8->64 + TBitMap<160, ui64> bitmap16(bitmap12); + CHECK_BITMAP(bitmap16, initBits); + } + + Y_UNIT_TEST(TestOps) { + TBitMap<16> bitmap1; + TBitMap<12> bitmap2; + size_t initBits1[] = {0, 3, 7, 11}; + size_t initBits2[] = {1, 3, 6, 7, 11}; + INIT_BITMAP(bitmap1, initBits1); + INIT_BITMAP(bitmap2, initBits2); + + bitmap1.Or(3).And(bitmap2).Flip(); + + size_t resBits[] = {0, 2, 4, 5, 6, 8, 9, 10, 12}; + CHECK_BITMAP_WITH_TAIL(bitmap1, resBits); + + TDynBitMap bitmap3; + INIT_BITMAP(bitmap3, initBits1); + + bitmap3.Or(3).And(bitmap2).Flip(); + + CHECK_BITMAP_WITH_TAIL(bitmap3, resBits); + + bitmap3.Clear(); + INIT_BITMAP(bitmap3, initBits1); + + TDynBitMap bitmap4 = ~((bitmap3 | 3) & bitmap2); + CHECK_BITMAP_WITH_TAIL(bitmap4, resBits); + + TBitMap<128, ui32> expmap; + expmap.Set(47); + expmap.Set(90); + ui64 tst1 = 0; + ui32 tst2 = 0; + ui16 tst3 = 0; + expmap.Export(32, tst1); + UNIT_ASSERT_EQUAL(tst1, (1 << 15) | (((ui64)1) << 58)); + expmap.Export(32, tst2); + UNIT_ASSERT_EQUAL(tst2, (1 << 15)); + expmap.Export(32, tst3); + UNIT_ASSERT_EQUAL(tst3, (1 << 15)); + + expmap.Export(33, tst1); + UNIT_ASSERT_EQUAL(tst1, (1 << 14) | (((ui64)1) << 57)); + expmap.Export(33, tst2); + UNIT_ASSERT_EQUAL(tst2, (1 << 14)); + expmap.Export(33, tst3); + UNIT_ASSERT_EQUAL(tst3, (1 << 14)); + } + + Y_UNIT_TEST(TestShiftFixed) { + size_t initBits[] = {0, 3, 7, 11}; + + TBitMap<128> bitmap1; + + INIT_BITMAP(bitmap1, initBits); + bitmap1 <<= 62; + size_t resBits1[] = {62, 65, 69, 73}; + CHECK_BITMAP(bitmap1, resBits1); + bitmap1 >>= 62; + CHECK_BITMAP(bitmap1, initBits); + + bitmap1.Clear(); + INIT_BITMAP(bitmap1, initBits); + bitmap1 <<= 120; + size_t resBits2[] = {120, 123, 127}; + CHECK_BITMAP(bitmap1, resBits2); + bitmap1 >>= 120; + size_t resBits3[] = {0, 3, 7}; + CHECK_BITMAP(bitmap1, resBits3); + + bitmap1.Clear(); + INIT_BITMAP(bitmap1, initBits); + bitmap1 <<= 128; + UNIT_ASSERT_EQUAL(bitmap1.Empty(), true); + + bitmap1.Clear(); + INIT_BITMAP(bitmap1, initBits); + bitmap1 <<= 120; + bitmap1 >>= 128; + UNIT_ASSERT_EQUAL(bitmap1.Empty(), true); + + bitmap1.Clear(); + INIT_BITMAP(bitmap1, initBits); + bitmap1 <<= 140; + UNIT_ASSERT_EQUAL(bitmap1.Empty(), true); + + bitmap1.Clear(); + INIT_BITMAP(bitmap1, initBits); + bitmap1 <<= 62; + bitmap1 >>= 140; + UNIT_ASSERT_EQUAL(bitmap1.Empty(), true); + } + + Y_UNIT_TEST(TestShiftDyn) { + size_t initBits[] = {0, 3, 7, 11}; + + TDynBitMap bitmap1; + + INIT_BITMAP(bitmap1, initBits); + bitmap1 <<= 62; + size_t resBits1[] = {62, 65, 69, 73}; + CHECK_BITMAP(bitmap1, resBits1); + bitmap1 >>= 62; + CHECK_BITMAP(bitmap1, initBits); + + bitmap1.Clear(); + INIT_BITMAP(bitmap1, initBits); + bitmap1 <<= 120; + size_t resBits2[] = {120, 123, 127, 131}; + CHECK_BITMAP(bitmap1, resBits2); + bitmap1 >>= 120; + CHECK_BITMAP(bitmap1, initBits); + + bitmap1.Clear(); + INIT_BITMAP(bitmap1, initBits); + bitmap1 <<= 128; + size_t resBits3[] = {128, 131, 135, 139}; + CHECK_BITMAP(bitmap1, resBits3); + + bitmap1.Clear(); + INIT_BITMAP(bitmap1, initBits); + bitmap1 <<= 120; + bitmap1 >>= 128; + size_t resBits4[] = {3}; + CHECK_BITMAP(bitmap1, resBits4); + + bitmap1.Clear(); + INIT_BITMAP(bitmap1, initBits); + bitmap1 <<= 62; + bitmap1 >>= 140; + UNIT_ASSERT_EQUAL(bitmap1.Empty(), true); + } + + // Test that we don't expand bitmap in LShift when high-order bits are zero + Y_UNIT_TEST(TestShiftExpansion) { + UNIT_ASSERT_EQUAL(TDynBitMap().LShift(1).Size(), 64); + UNIT_ASSERT_EQUAL(TDynBitMap().LShift(65).Size(), 64); + UNIT_ASSERT_EQUAL(TDynBitMap().LShift(128).Size(), 64); + + TDynBitMap bitmap; + bitmap.Set(62).LShift(1); + UNIT_ASSERT_EQUAL(bitmap, TDynBitMap().Set(63)); + UNIT_ASSERT_EQUAL(bitmap.Size(), 64); + + // Expand explicitly + bitmap.Set(65); + UNIT_ASSERT_EQUAL(bitmap.Size(), 128); + + bitmap.Clear().Set(0).LShift(1); + UNIT_ASSERT_EQUAL(bitmap, TDynBitMap().Set(1)); + UNIT_ASSERT_EQUAL(bitmap.Size(), 128); + + bitmap.Clear().Set(63).LShift(1); + UNIT_ASSERT_EQUAL(bitmap, TDynBitMap().Set(64)); + UNIT_ASSERT_EQUAL(bitmap.Size(), 128); + + bitmap.Clear().Set(63).LShift(64); + UNIT_ASSERT_EQUAL(bitmap, TDynBitMap().Set(127)); + UNIT_ASSERT_EQUAL(bitmap.Size(), 128); + + bitmap.Clear().Set(62).LShift(129); + UNIT_ASSERT_EQUAL(bitmap, TDynBitMap().Set(191)); + UNIT_ASSERT_EQUAL(bitmap.Size(), 256); + } + + Y_UNIT_TEST(TestFixedSanity) { + // test extra-bit cleanup + UNIT_ASSERT_EQUAL(TBitMap<33>().Set(0).LShift(34).RShift(34).Empty(), true); + UNIT_ASSERT_EQUAL(TBitMap<88>().Set(0).Set(1).Set(2).LShift(90).RShift(90).Empty(), true); + UNIT_ASSERT_EQUAL(TBitMap<88>().Flip().RShift(88).Empty(), true); + UNIT_ASSERT_EQUAL(TBitMap<64>().Flip().LShift(2).RShift(2).Count(), 62); + UNIT_ASSERT_EQUAL(TBitMap<67>().Flip().LShift(2).RShift(2).Count(), 65); + UNIT_ASSERT_EQUAL(TBitMap<128>().Flip().LShift(2).RShift(2).Count(), 126); + UNIT_ASSERT_EQUAL(TBitMap<130>().Flip().LShift(2).RShift(2).Count(), 128); + UNIT_ASSERT_EQUAL(TBitMap<130>(TDynBitMap().Set(131)).Empty(), true); + UNIT_ASSERT_EQUAL(TBitMap<33>().Or(TBitMap<40>().Set(39)).Empty(), true); + UNIT_ASSERT_EQUAL(TBitMap<33>().Xor(TBitMap<40>().Set(39)).Empty(), true); + } + + Y_UNIT_TEST(TestIterate) { + TDynBitMap bitmap1; + TDynBitMap bitmap2; + + size_t initBits1[] = {0, 3, 7, 8, 11, 33, 34, 35, 36, 62, 63, 100, 127}; + INIT_BITMAP(bitmap1, initBits1); + for (size_t i = bitmap1.FirstNonZeroBit(); i != bitmap1.Size(); i = bitmap1.NextNonZeroBit(i)) { + bitmap2.Set(i); + } + CHECK_BITMAP(bitmap2, initBits1); + UNIT_ASSERT_EQUAL(bitmap1, bitmap2); + + size_t initBits2[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 33, 34, 35, 36, 62}; + bitmap1.Clear(); + bitmap2.Clear(); + INIT_BITMAP(bitmap1, initBits2); + for (size_t i = bitmap1.FirstNonZeroBit(); i != bitmap1.Size(); i = bitmap1.NextNonZeroBit(i)) { + bitmap2.Set(i); + } + CHECK_BITMAP(bitmap2, initBits2); + UNIT_ASSERT_EQUAL(bitmap1, bitmap2); + + UNIT_ASSERT_EQUAL(bitmap1.NextNonZeroBit(63), bitmap1.Size()); + UNIT_ASSERT_EQUAL(bitmap1.NextNonZeroBit(64), bitmap1.Size()); + UNIT_ASSERT_EQUAL(bitmap1.NextNonZeroBit(65), bitmap1.Size()); + UNIT_ASSERT_EQUAL(bitmap1.NextNonZeroBit(127), bitmap1.Size()); + UNIT_ASSERT_EQUAL(bitmap1.NextNonZeroBit(533), bitmap1.Size()); + + TBitMap<128, ui8> bitmap3; + bitmap1.Clear(); + INIT_BITMAP(bitmap1, initBits1); + for (size_t i = bitmap1.FirstNonZeroBit(); i != bitmap1.Size(); i = bitmap1.NextNonZeroBit(i)) { + bitmap3.Set(i); + } + CHECK_BITMAP(bitmap3, initBits1); + UNIT_ASSERT_EQUAL(bitmap3, bitmap1); + + TBitMap<18> bitmap4; + bitmap4.Set(15); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(0), 15); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(15), bitmap4.Size()); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(63), bitmap4.Size()); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(64), bitmap4.Size()); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(65), bitmap4.Size()); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(127), bitmap4.Size()); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(533), bitmap4.Size()); + + bitmap4.Clear().Flip(); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(0), 1); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(15), 16); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(17), bitmap4.Size()); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(18), bitmap4.Size()); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(63), bitmap4.Size()); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(64), bitmap4.Size()); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(65), bitmap4.Size()); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(127), bitmap4.Size()); + UNIT_ASSERT_EQUAL(bitmap4.NextNonZeroBit(533), bitmap4.Size()); + } + + Y_UNIT_TEST(TestHashFixed) { + TBitMap<32, ui8> bitmap32; + TBitMap<32, ui8> bitmap322; + TBitMap<64, ui8> bitmap64; + + bitmap32.Clear(); + bitmap322.Clear(); + UNIT_ASSERT_EQUAL(bitmap32.Hash(), bitmap322.Hash()); + bitmap32.Set(0); + UNIT_ASSERT_UNEQUAL(bitmap32.Hash(), bitmap322.Hash()); + bitmap322.Set(0); + UNIT_ASSERT_EQUAL(bitmap32.Hash(), bitmap322.Hash()); + bitmap32.Set(8).Set(31); + UNIT_ASSERT_UNEQUAL(bitmap32.Hash(), bitmap322.Hash()); + bitmap322.Set(8).Set(31); + UNIT_ASSERT_EQUAL(bitmap32.Hash(), bitmap322.Hash()); + + bitmap32.Clear(); + bitmap64.Clear(); + UNIT_ASSERT_EQUAL(bitmap32.Hash(), bitmap64.Hash()); + bitmap32.Set(0); + UNIT_ASSERT_UNEQUAL(bitmap32.Hash(), bitmap64.Hash()); + bitmap64.Set(0); + UNIT_ASSERT_EQUAL(bitmap32.Hash(), bitmap64.Hash()); + bitmap32.Set(8).Set(31); + UNIT_ASSERT_UNEQUAL(bitmap32.Hash(), bitmap64.Hash()); + bitmap64.Set(8).Set(31); + UNIT_ASSERT_EQUAL(bitmap32.Hash(), bitmap64.Hash()); + bitmap64.Set(32); + UNIT_ASSERT_UNEQUAL(bitmap32.Hash(), bitmap64.Hash()); + } + + Y_UNIT_TEST(TestHashDynamic) { + TDynBitMap bitmap1; + TDynBitMap bitmap2; + + bitmap1.Clear(); + bitmap2.Clear(); + UNIT_ASSERT_EQUAL(bitmap1.Hash(), bitmap2.Hash()); + bitmap1.Set(0); + UNIT_ASSERT_UNEQUAL(bitmap1.Hash(), bitmap2.Hash()); + bitmap2.Set(0); + UNIT_ASSERT_EQUAL(bitmap1.Hash(), bitmap2.Hash()); + bitmap1.Set(8).Set(31); + UNIT_ASSERT_UNEQUAL(bitmap1.Hash(), bitmap2.Hash()); + bitmap2.Set(8).Set(31); + UNIT_ASSERT_EQUAL(bitmap1.Hash(), bitmap2.Hash()); + bitmap1.Set(64); + UNIT_ASSERT_UNEQUAL(bitmap1.Hash(), bitmap2.Hash()); + bitmap2.Set(64); + UNIT_ASSERT_EQUAL(bitmap1.Hash(), bitmap2.Hash()); + } + + Y_UNIT_TEST(TestHashMixed) { + static_assert((std::is_same::value), "expect (TSameType::Result)"); + + TBitMap bitmapFixed; + TDynBitMap bitmapDynamic; + + bitmapFixed.Clear(); + bitmapDynamic.Clear(); + UNIT_ASSERT_EQUAL(bitmapFixed.Hash(), bitmapDynamic.Hash()); + bitmapFixed.Set(0); + UNIT_ASSERT_UNEQUAL(bitmapFixed.Hash(), bitmapDynamic.Hash()); + bitmapDynamic.Set(0); + UNIT_ASSERT_EQUAL(bitmapFixed.Hash(), bitmapDynamic.Hash()); + bitmapFixed.Set(8).Set(127); + UNIT_ASSERT_UNEQUAL(bitmapFixed.Hash(), bitmapDynamic.Hash()); + bitmapDynamic.Set(8).Set(127); + UNIT_ASSERT_EQUAL(bitmapFixed.Hash(), bitmapDynamic.Hash()); + bitmapDynamic.Set(128); + UNIT_ASSERT_UNEQUAL(bitmapFixed.Hash(), bitmapDynamic.Hash()); + } + + Y_UNIT_TEST(TestSetResetRange) { + // Single chunk + using TBitMap1Chunk = TBitMap<64>; + UNIT_ASSERT_EQUAL(TBitMap1Chunk().Flip().Reset(10, 50), TBitMap1Chunk().Set(0, 10).Set(50, 64)); + UNIT_ASSERT_EQUAL(TBitMap1Chunk().Flip().Reset(0, 10), TBitMap1Chunk().Set(10, 64)); + UNIT_ASSERT_EQUAL(TBitMap1Chunk().Flip().Reset(50, 64), TBitMap1Chunk().Set(0, 50)); + UNIT_ASSERT_EQUAL(TBitMap1Chunk().Flip().Reset(0, 10).Reset(50, 64), TBitMap1Chunk().Set(10, 50)); + + // Two chunks + using TBitMap2Chunks = TBitMap<64, ui32>; + UNIT_ASSERT_EQUAL(TBitMap2Chunks().Flip().Reset(10, 50), TBitMap2Chunks().Set(0, 10).Set(50, 64)); + UNIT_ASSERT_EQUAL(TBitMap2Chunks().Flip().Reset(0, 10), TBitMap2Chunks().Set(10, 64)); + UNIT_ASSERT_EQUAL(TBitMap2Chunks().Flip().Reset(50, 64), TBitMap2Chunks().Set(0, 50)); + UNIT_ASSERT_EQUAL(TBitMap2Chunks().Flip().Reset(0, 10).Reset(50, 64), TBitMap2Chunks().Set(10, 50)); + + // Many chunks + using TBitMap4Chunks = TBitMap<64, ui16>; + UNIT_ASSERT_EQUAL(TBitMap4Chunks().Flip().Reset(10, 50), TBitMap4Chunks().Set(0, 10).Set(50, 64)); + UNIT_ASSERT_EQUAL(TBitMap4Chunks().Flip().Reset(0, 10), TBitMap4Chunks().Set(10, 64)); + UNIT_ASSERT_EQUAL(TBitMap4Chunks().Flip().Reset(50, 64), TBitMap4Chunks().Set(0, 50)); + UNIT_ASSERT_EQUAL(TBitMap4Chunks().Flip().Reset(0, 10).Reset(50, 64), TBitMap4Chunks().Set(10, 50)); + } + + Y_UNIT_TEST(TestSetRangeDyn) { + for (size_t start = 0; start < 192; ++start) { + for (size_t end = start; end < 192; ++end) { + TDynBitMap bm; + bm.Reserve(192); + bm.Set(start, end); + for (size_t k = 0; k < 192; ++k) { + UNIT_ASSERT_VALUES_EQUAL(bm.Get(k), k >= start && k < end ? 1 : 0); + } + } + } + } + + Y_UNIT_TEST(TestResetLargeRangeDyn) { + TDynBitMap bm; + bm.Set(0); + bm.Reset(1, 2048); + bm.Set(2048); + for (size_t k = 0; k <= 2048; ++k) { + UNIT_ASSERT_VALUES_EQUAL(bm.Get(k), k >= 1 && k < 2048 ? 0 : 1); + } + } +} diff --git a/util/generic/bitops.cpp b/util/generic/bitops.cpp new file mode 100644 index 00000000000..db5667a21f6 --- /dev/null +++ b/util/generic/bitops.cpp @@ -0,0 +1,141 @@ +#include "bitops.h" + +namespace NBitOps { + namespace NPrivate { + const ui64 WORD_MASK[] = { + 0x0000000000000000ULL, + 0x0000000000000001ULL, + 0x0000000000000003ULL, + 0x0000000000000007ULL, + 0x000000000000000FULL, + 0x000000000000001FULL, + 0x000000000000003FULL, + 0x000000000000007FULL, + 0x00000000000000FFULL, + 0x00000000000001FFULL, + 0x00000000000003FFULL, + 0x00000000000007FFULL, + 0x0000000000000FFFULL, + 0x0000000000001FFFULL, + 0x0000000000003FFFULL, + 0x0000000000007FFFULL, + 0x000000000000FFFFULL, + 0x000000000001FFFFULL, + 0x000000000003FFFFULL, + 0x000000000007FFFFULL, + 0x00000000000FFFFFULL, + 0x00000000001FFFFFULL, + 0x00000000003FFFFFULL, + 0x00000000007FFFFFULL, + 0x0000000000FFFFFFULL, + 0x0000000001FFFFFFULL, + 0x0000000003FFFFFFULL, + 0x0000000007FFFFFFULL, + 0x000000000FFFFFFFULL, + 0x000000001FFFFFFFULL, + 0x000000003FFFFFFFULL, + 0x000000007FFFFFFFULL, + 0x00000000FFFFFFFFULL, + 0x00000001FFFFFFFFULL, + 0x00000003FFFFFFFFULL, + 0x00000007FFFFFFFFULL, + 0x0000000FFFFFFFFFULL, + 0x0000001FFFFFFFFFULL, + 0x0000003FFFFFFFFFULL, + 0x0000007FFFFFFFFFULL, + 0x000000FFFFFFFFFFULL, + 0x000001FFFFFFFFFFULL, + 0x000003FFFFFFFFFFULL, + 0x000007FFFFFFFFFFULL, + 0x00000FFFFFFFFFFFULL, + 0x00001FFFFFFFFFFFULL, + 0x00003FFFFFFFFFFFULL, + 0x00007FFFFFFFFFFFULL, + 0x0000FFFFFFFFFFFFULL, + 0x0001FFFFFFFFFFFFULL, + 0x0003FFFFFFFFFFFFULL, + 0x0007FFFFFFFFFFFFULL, + 0x000FFFFFFFFFFFFFULL, + 0x001FFFFFFFFFFFFFULL, + 0x003FFFFFFFFFFFFFULL, + 0x007FFFFFFFFFFFFFULL, + 0x00FFFFFFFFFFFFFFULL, + 0x01FFFFFFFFFFFFFFULL, + 0x03FFFFFFFFFFFFFFULL, + 0x07FFFFFFFFFFFFFFULL, + 0x0FFFFFFFFFFFFFFFULL, + 0x1FFFFFFFFFFFFFFFULL, + 0x3FFFFFFFFFFFFFFFULL, + 0x7FFFFFFFFFFFFFFFULL, + 0xFFFFFFFFFFFFFFFFULL, + }; + + const ui64 INVERSE_WORD_MASK[] = { + ~0x0000000000000000ULL, + ~0x0000000000000001ULL, + ~0x0000000000000003ULL, + ~0x0000000000000007ULL, + ~0x000000000000000FULL, + ~0x000000000000001FULL, + ~0x000000000000003FULL, + ~0x000000000000007FULL, + ~0x00000000000000FFULL, + ~0x00000000000001FFULL, + ~0x00000000000003FFULL, + ~0x00000000000007FFULL, + ~0x0000000000000FFFULL, + ~0x0000000000001FFFULL, + ~0x0000000000003FFFULL, + ~0x0000000000007FFFULL, + ~0x000000000000FFFFULL, + ~0x000000000001FFFFULL, + ~0x000000000003FFFFULL, + ~0x000000000007FFFFULL, + ~0x00000000000FFFFFULL, + ~0x00000000001FFFFFULL, + ~0x00000000003FFFFFULL, + ~0x00000000007FFFFFULL, + ~0x0000000000FFFFFFULL, + ~0x0000000001FFFFFFULL, + ~0x0000000003FFFFFFULL, + ~0x0000000007FFFFFFULL, + ~0x000000000FFFFFFFULL, + ~0x000000001FFFFFFFULL, + ~0x000000003FFFFFFFULL, + ~0x000000007FFFFFFFULL, + ~0x00000000FFFFFFFFULL, + ~0x00000001FFFFFFFFULL, + ~0x00000003FFFFFFFFULL, + ~0x00000007FFFFFFFFULL, + ~0x0000000FFFFFFFFFULL, + ~0x0000001FFFFFFFFFULL, + ~0x0000003FFFFFFFFFULL, + ~0x0000007FFFFFFFFFULL, + ~0x000000FFFFFFFFFFULL, + ~0x000001FFFFFFFFFFULL, + ~0x000003FFFFFFFFFFULL, + ~0x000007FFFFFFFFFFULL, + ~0x00000FFFFFFFFFFFULL, + ~0x00001FFFFFFFFFFFULL, + ~0x00003FFFFFFFFFFFULL, + ~0x00007FFFFFFFFFFFULL, + ~0x0000FFFFFFFFFFFFULL, + ~0x0001FFFFFFFFFFFFULL, + ~0x0003FFFFFFFFFFFFULL, + ~0x0007FFFFFFFFFFFFULL, + ~0x000FFFFFFFFFFFFFULL, + ~0x001FFFFFFFFFFFFFULL, + ~0x003FFFFFFFFFFFFFULL, + ~0x007FFFFFFFFFFFFFULL, + ~0x00FFFFFFFFFFFFFFULL, + ~0x01FFFFFFFFFFFFFFULL, + ~0x03FFFFFFFFFFFFFFULL, + ~0x07FFFFFFFFFFFFFFULL, + ~0x0FFFFFFFFFFFFFFFULL, + ~0x1FFFFFFFFFFFFFFFULL, + ~0x3FFFFFFFFFFFFFFFULL, + ~0x7FFFFFFFFFFFFFFFULL, + ~0xFFFFFFFFFFFFFFFFULL, + }; + } +} diff --git a/util/generic/bitops.h b/util/generic/bitops.h new file mode 100644 index 00000000000..2db15fc59b6 --- /dev/null +++ b/util/generic/bitops.h @@ -0,0 +1,459 @@ +#pragma once + +#include "ylimits.h" +#include "typelist.h" + +#include +#include + +#ifdef _MSC_VER + #include +#endif + +namespace NBitOps { + namespace NPrivate { + template + struct TClp2Helper { + static Y_FORCE_INLINE T Calc(T t) noexcept { + const T prev = TClp2Helper::Calc(t); + + return prev | (prev >> N); + } + }; + + template + struct TClp2Helper<0u, T> { + static Y_FORCE_INLINE T Calc(T t) noexcept { + return t - 1; + } + }; + + extern const ui64 WORD_MASK[]; + extern const ui64 INVERSE_WORD_MASK[]; + + // see http://www-graphics.stanford.edu/~seander/bithacks.html#ReverseParallel + + Y_FORCE_INLINE ui64 SwapOddEvenBits(ui64 v) { + return ((v >> 1ULL) & 0x5555555555555555ULL) | ((v & 0x5555555555555555ULL) << 1ULL); + } + + Y_FORCE_INLINE ui64 SwapBitPairs(ui64 v) { + return ((v >> 2ULL) & 0x3333333333333333ULL) | ((v & 0x3333333333333333ULL) << 2ULL); + } + + Y_FORCE_INLINE ui64 SwapNibbles(ui64 v) { + return ((v >> 4ULL) & 0x0F0F0F0F0F0F0F0FULL) | ((v & 0x0F0F0F0F0F0F0F0FULL) << 4ULL); + } + + Y_FORCE_INLINE ui64 SwapOddEvenBytes(ui64 v) { + return ((v >> 8ULL) & 0x00FF00FF00FF00FFULL) | ((v & 0x00FF00FF00FF00FFULL) << 8ULL); + } + + Y_FORCE_INLINE ui64 SwapBytePairs(ui64 v) { + return ((v >> 16ULL) & 0x0000FFFF0000FFFFULL) | ((v & 0x0000FFFF0000FFFFULL) << 16ULL); + } + + Y_FORCE_INLINE ui64 SwapByteQuads(ui64 v) { + return (v >> 32ULL) | (v << 32ULL); + } + +#if defined(__GNUC__) + inline unsigned GetValueBitCountImpl(unsigned int value) noexcept { + Y_ASSERT(value); // because __builtin_clz* have undefined result for zero. + return std::numeric_limits::digits - __builtin_clz(value); + } + + inline unsigned GetValueBitCountImpl(unsigned long value) noexcept { + Y_ASSERT(value); // because __builtin_clz* have undefined result for zero. + return std::numeric_limits::digits - __builtin_clzl(value); + } + + inline unsigned GetValueBitCountImpl(unsigned long long value) noexcept { + Y_ASSERT(value); // because __builtin_clz* have undefined result for zero. + return std::numeric_limits::digits - __builtin_clzll(value); + } +#else + /// Stupid realization for non-GCC. Can use BSR from x86 instructions set. + template + inline unsigned GetValueBitCountImpl(T value) noexcept { + Y_ASSERT(value); // because __builtin_clz* have undefined result for zero. + unsigned result = 1; // result == 0 - impossible value, see Y_ASSERT(). + value >>= 1; + while (value) { + value >>= 1; + ++result; + } + + return result; + } +#endif + +#if defined(__GNUC__) + inline unsigned CountTrailingZeroBitsImpl(unsigned int value) noexcept { + Y_ASSERT(value); // because __builtin_ctz* have undefined result for zero. + return __builtin_ctz(value); + } + + inline unsigned CountTrailingZeroBitsImpl(unsigned long value) noexcept { + Y_ASSERT(value); // because __builtin_ctz* have undefined result for zero. + return __builtin_ctzl(value); + } + + inline unsigned CountTrailingZeroBitsImpl(unsigned long long value) noexcept { + Y_ASSERT(value); // because __builtin_ctz* have undefined result for zero. + return __builtin_ctzll(value); + } +#else + /// Stupid realization for non-GCC. Can use BSF from x86 instructions set. + template + inline unsigned CountTrailingZeroBitsImpl(T value) noexcept { + Y_ASSERT(value); // because __builtin_ctz* have undefined result for zero. + unsigned result = 0; + while (!(value & 1)) { + value >>= 1; + ++result; + } + + return result; + } +#endif + + template + Y_FORCE_INLINE T RotateBitsLeftImpl(T value, const ui8 shift) noexcept { + constexpr ui8 bits = sizeof(T) * 8; + constexpr ui8 mask = bits - 1; + Y_ASSERT(shift <= mask); + + // do trick with mask to avoid undefined behaviour + return (value << shift) | (value >> ((-shift) & mask)); + } + + template + Y_FORCE_INLINE T RotateBitsRightImpl(T value, const ui8 shift) noexcept { + constexpr ui8 bits = sizeof(T) * 8; + constexpr ui8 mask = bits - 1; + Y_ASSERT(shift <= mask); + + // do trick with mask to avoid undefined behaviour + return (value >> shift) | (value << ((-shift) & mask)); + } + +#if defined(_x86_) && defined(__GNUC__) + Y_FORCE_INLINE ui8 RotateBitsRightImpl(ui8 value, ui8 shift) noexcept { + __asm__("rorb %%cl, %0" + : "=r"(value) + : "0"(value), "c"(shift)); + return value; + } + + Y_FORCE_INLINE ui16 RotateBitsRightImpl(ui16 value, ui8 shift) noexcept { + __asm__("rorw %%cl, %0" + : "=r"(value) + : "0"(value), "c"(shift)); + return value; + } + + Y_FORCE_INLINE ui32 RotateBitsRightImpl(ui32 value, ui8 shift) noexcept { + __asm__("rorl %%cl, %0" + : "=r"(value) + : "0"(value), "c"(shift)); + return value; + } + + Y_FORCE_INLINE ui8 RotateBitsLeftImpl(ui8 value, ui8 shift) noexcept { + __asm__("rolb %%cl, %0" + : "=r"(value) + : "0"(value), "c"(shift)); + return value; + } + + Y_FORCE_INLINE ui16 RotateBitsLeftImpl(ui16 value, ui8 shift) noexcept { + __asm__("rolw %%cl, %0" + : "=r"(value) + : "0"(value), "c"(shift)); + return value; + } + + Y_FORCE_INLINE ui32 RotateBitsLeftImpl(ui32 value, ui8 shift) noexcept { + __asm__("roll %%cl, %0" + : "=r"(value) + : "0"(value), "c"(shift)); + return value; + } + + #if defined(_x86_64_) + Y_FORCE_INLINE ui64 RotateBitsRightImpl(ui64 value, ui8 shift) noexcept { + __asm__("rorq %%cl, %0" + : "=r"(value) + : "0"(value), "c"(shift)); + return value; + } + + Y_FORCE_INLINE ui64 RotateBitsLeftImpl(ui64 value, ui8 shift) noexcept { + __asm__("rolq %%cl, %0" + : "=r"(value) + : "0"(value), "c"(shift)); + return value; + } + #endif +#endif + } +} + +/** + * Computes the next power of 2 higher or equal to the integer parameter `t`. + * If `t` is a power of 2 will return `t`. + * Result is undefined for `t == 0`. + */ +template +static inline T FastClp2(T t) noexcept { + Y_ASSERT(t > 0); + using TCvt = typename ::TUnsignedInts::template TSelectBy::template TResult>::type; + return 1 + ::NBitOps::NPrivate::TClp2Helper::Calc(static_cast(t)); +} + +/** + * Check if integer is a power of 2. + */ +template +Y_CONST_FUNCTION constexpr bool IsPowerOf2(T v) noexcept { + return v > 0 && (v & (v - 1)) == 0; +} + +/** + * Returns the number of leading 0-bits in `value`, starting at the most significant bit position. + */ +template +static inline unsigned GetValueBitCount(T value) noexcept { + Y_ASSERT(value > 0); + using TCvt = typename ::TUnsignedInts::template TSelectBy::template TResult>::type; + return ::NBitOps::NPrivate::GetValueBitCountImpl(static_cast(value)); +} + +/** + * Returns the number of trailing 0-bits in `value`, starting at the least significant bit position + */ +template +static inline unsigned CountTrailingZeroBits(T value) noexcept { + Y_ASSERT(value > 0); + using TCvt = typename ::TUnsignedInts::template TSelectBy::template TResult>::type; + return ::NBitOps::NPrivate::CountTrailingZeroBitsImpl(static_cast(value)); +} + +/* + * Returns 64-bit mask with `bits` lower bits set. + */ +Y_FORCE_INLINE ui64 MaskLowerBits(ui64 bits) { + return ::NBitOps::NPrivate::WORD_MASK[bits]; +} + +/* + * Return 64-bit mask with `bits` set starting from `skipbits`. + */ +Y_FORCE_INLINE ui64 MaskLowerBits(ui64 bits, ui64 skipbits) { + return MaskLowerBits(bits) << skipbits; +} + +/* + * Return 64-bit mask with all bits set except for `bits` lower bits. + */ +Y_FORCE_INLINE ui64 InverseMaskLowerBits(ui64 bits) { + return ::NBitOps::NPrivate::INVERSE_WORD_MASK[bits]; +} + +/* + * Return 64-bit mask with all bits set except for `bits` bitst starting from `skipbits`. + */ +Y_FORCE_INLINE ui64 InverseMaskLowerBits(ui64 bits, ui64 skipbits) { + return ~MaskLowerBits(bits, skipbits); +} + +/* + * Returns 0-based position of the most significant bit that is set. 0 for 0. + */ +Y_FORCE_INLINE ui64 MostSignificantBit(ui64 v) { +#ifdef __GNUC__ + ui64 res = v ? (63 - __builtin_clzll(v)) : 0; +#elif defined(_MSC_VER) && defined(_64_) + unsigned long res = 0; + if (v) + _BitScanReverse64(&res, v); +#else + ui64 res = 0; + if (v) + while (v >>= 1) + ++res; +#endif + return res; +} + +/** + * Returns 0-based position of the least significant bit that is set. 0 for 0. + */ +Y_FORCE_INLINE ui64 LeastSignificantBit(ui64 v) { +#ifdef __GNUC__ + ui64 res = v ? __builtin_ffsll(v) - 1 : 0; +#elif defined(_MSC_VER) && defined(_64_) + unsigned long res = 0; + if (v) + _BitScanForward64(&res, v); +#else + ui64 res = 0; + if (v) { + while (!(v & 1)) { + ++res; + v >>= 1; + } + } +#endif + return res; +} + +/* + * Returns 0 - based position of the most significant bit (compile time) + * 0 for 0. + */ +constexpr ui64 MostSignificantBitCT(ui64 x) { + return x > 1 ? 1 + MostSignificantBitCT(x >> 1) : 0; +} + +/* + * Return rounded up binary logarithm of `x`. + */ +Y_FORCE_INLINE ui8 CeilLog2(ui64 x) { + return static_cast(MostSignificantBit(x - 1)) + 1; +} + +Y_FORCE_INLINE ui8 ReverseBytes(ui8 t) { + return t; +} + +Y_FORCE_INLINE ui16 ReverseBytes(ui16 t) { + return static_cast(::NBitOps::NPrivate::SwapOddEvenBytes(t)); +} + +Y_FORCE_INLINE ui32 ReverseBytes(ui32 t) { + return static_cast(::NBitOps::NPrivate::SwapBytePairs( + ::NBitOps::NPrivate::SwapOddEvenBytes(t))); +} + +Y_FORCE_INLINE ui64 ReverseBytes(ui64 t) { + return ::NBitOps::NPrivate::SwapByteQuads((::NBitOps::NPrivate::SwapOddEvenBytes(t))); +} + +Y_FORCE_INLINE ui8 ReverseBits(ui8 t) { + return static_cast(::NBitOps::NPrivate::SwapNibbles( + ::NBitOps::NPrivate::SwapBitPairs( + ::NBitOps::NPrivate::SwapOddEvenBits(t)))); +} + +Y_FORCE_INLINE ui16 ReverseBits(ui16 t) { + return static_cast(::NBitOps::NPrivate::SwapOddEvenBytes( + ::NBitOps::NPrivate::SwapNibbles( + ::NBitOps::NPrivate::SwapBitPairs( + ::NBitOps::NPrivate::SwapOddEvenBits(t))))); +} + +Y_FORCE_INLINE ui32 ReverseBits(ui32 t) { + return static_cast(::NBitOps::NPrivate::SwapBytePairs( + ::NBitOps::NPrivate::SwapOddEvenBytes( + ::NBitOps::NPrivate::SwapNibbles( + ::NBitOps::NPrivate::SwapBitPairs( + ::NBitOps::NPrivate::SwapOddEvenBits(t)))))); +} + +Y_FORCE_INLINE ui64 ReverseBits(ui64 t) { + return ::NBitOps::NPrivate::SwapByteQuads( + ::NBitOps::NPrivate::SwapBytePairs( + ::NBitOps::NPrivate::SwapOddEvenBytes( + ::NBitOps::NPrivate::SwapNibbles( + ::NBitOps::NPrivate::SwapBitPairs( + ::NBitOps::NPrivate::SwapOddEvenBits(t)))))); +} + +/* + * Reverse first `bits` bits + * 1000111000111000 , bits = 6 => 1000111000000111 + */ +template +Y_FORCE_INLINE T ReverseBits(T v, ui64 bits) { + return bits ? (T(v & ::InverseMaskLowerBits(bits)) | T(ReverseBits(T(v & ::MaskLowerBits(bits)))) >> ((ui64{sizeof(T)} << ui64{3}) - bits)) : v; +} + +/* + * Referse first `bits` bits starting from `skipbits` bits + * 1000111000111000 , bits = 4, skipbits = 2 => 1000111000011100 + */ +template +Y_FORCE_INLINE T ReverseBits(T v, ui64 bits, ui64 skipbits) { + return (T(ReverseBits((v >> skipbits), bits)) << skipbits) | T(v & MaskLowerBits(skipbits)); +} + +/* Rotate bits left. Also known as left circular shift. + */ +template +Y_FORCE_INLINE T RotateBitsLeft(T value, const ui8 shift) noexcept { + static_assert(std::is_unsigned::value, "must be unsigned arithmetic type"); + return ::NBitOps::NPrivate::RotateBitsLeftImpl((TFixedWidthUnsignedInt)value, shift); +} + +/* Rotate bits right. Also known as right circular shift. + */ +template +Y_FORCE_INLINE T RotateBitsRight(T value, const ui8 shift) noexcept { + static_assert(std::is_unsigned::value, "must be unsigned arithmetic type"); + return ::NBitOps::NPrivate::RotateBitsRightImpl((TFixedWidthUnsignedInt)value, shift); +} + +/* Rotate bits left. Also known as left circular shift. + */ +template +constexpr T RotateBitsLeftCT(T value, const ui8 shift) noexcept { + static_assert(std::is_unsigned::value, "must be unsigned arithmetic type"); + + // do trick with mask to avoid undefined behaviour + return (value << shift) | (value >> ((-shift) & (sizeof(T) * 8 - 1))); +} + +/* Rotate bits right. Also known as right circular shift. + */ +template +constexpr T RotateBitsRightCT(T value, const ui8 shift) noexcept { + static_assert(std::is_unsigned::value, "must be unsigned arithmetic type"); + + // do trick with mask to avoid undefined behaviour + return (value >> shift) | (value << ((-shift) & (sizeof(T) * 8 - 1))); +} + +/* Remain `size` bits to current `offset` of `value` + size, offset are less than number of bits in size_type + */ +template +Y_FORCE_INLINE T SelectBits(T value) { + static_assert(Size < sizeof(T) * 8, "violated: Size < sizeof(T) * 8"); + static_assert(Offset < sizeof(T) * 8, "violated: Offset < sizeof(T) * 8"); + T id = 1; + return (value >> Offset) & ((id << Size) - id); +} + +/* Set `size` bits of `bits` to current offset of `value`. Requires that bits <= (1 << size) - 1 + size, offset are less than number of bits in size_type + */ +template +void SetBits(T& value, T bits) { + static_assert(Size < sizeof(T) * 8, "violated: Size < sizeof(T) * 8"); + static_assert(Offset < sizeof(T) * 8, "violated: Offset < sizeof(T) * 8"); + T id = 1; + T maxValue = ((id << Size) - id); + Y_ASSERT(bits <= maxValue); + value &= ~(maxValue << Offset); + value |= bits << Offset; +} + +inline constexpr ui64 NthBit64(int bit) { + return ui64(1) << bit; +} + +inline constexpr ui64 Mask64(int bits) { + return NthBit64(bits) - 1; +} diff --git a/util/generic/bitops_ut.cpp b/util/generic/bitops_ut.cpp new file mode 100644 index 00000000000..90f4bc1c3ce --- /dev/null +++ b/util/generic/bitops_ut.cpp @@ -0,0 +1,348 @@ +#include "bitops.h" + +#include + +#include + +template +static void TestCTZ() { + for (unsigned int i = 0; i < (sizeof(T) << 3); ++i) { + UNIT_ASSERT_VALUES_EQUAL(CountTrailingZeroBits(T(1) << i), i); + } +} + +template +static void TestFastClp2ForEachPowerOf2() { + for (size_t i = 0; i < sizeof(T) * 8 - 1; ++i) { + const auto current = T(1) << i; + UNIT_ASSERT_VALUES_EQUAL(FastClp2(current), current); + } + + UNIT_ASSERT_VALUES_EQUAL(FastClp2(T(1)), T(1)); + for (size_t i = 1; i < sizeof(T) * 8 - 1; ++i) { + for (size_t j = 0; j < i; ++j) { + const auto value = (T(1) << i) | (T(1) << j); + const auto next = T(1) << (i + 1); + UNIT_ASSERT_VALUES_EQUAL(FastClp2(value), next); + } + } +} + +template +static T ReverseBitsSlow(T v) { + T r = v; // r will be reversed bits of v; first get LSB of v + ui32 s = sizeof(v) * 8 - 1; // extra shift needed at end + + for (v >>= 1; v; v >>= 1) { + r <<= 1; + r |= v & 1; + --s; + } + + r <<= s; // shift when v's highest bits are zero + return r; +} + +// DO_NOT_STYLE +Y_UNIT_TEST_SUITE(TBitOpsTest) { + Y_UNIT_TEST(TestCountTrailingZeroBits) { + TestCTZ(); + TestCTZ(); + TestCTZ(); + } + + Y_UNIT_TEST(TestIsPowerOf2) { + UNIT_ASSERT(!IsPowerOf2(-2)); + UNIT_ASSERT(!IsPowerOf2(-1)); + UNIT_ASSERT(!IsPowerOf2(0)); + UNIT_ASSERT(IsPowerOf2(1)); + UNIT_ASSERT(IsPowerOf2(2)); + UNIT_ASSERT(!IsPowerOf2(3)); + UNIT_ASSERT(IsPowerOf2(4)); + UNIT_ASSERT(!IsPowerOf2(5)); + UNIT_ASSERT(IsPowerOf2(0x10000000u)); + UNIT_ASSERT(!IsPowerOf2(0x10000001u)); + UNIT_ASSERT(IsPowerOf2(0x1000000000000000ull)); + UNIT_ASSERT(!IsPowerOf2(0x1000000000000001ull)); + } + + Y_UNIT_TEST(TestFastClp2) { + TestFastClp2ForEachPowerOf2(); + TestFastClp2ForEachPowerOf2(); + TestFastClp2ForEachPowerOf2(); + } + + Y_UNIT_TEST(TestMask) { + for (ui32 i = 0; i < 64; ++i) { + UNIT_ASSERT_VALUES_EQUAL(MaskLowerBits(i), (ui64{1} << i) - 1); + UNIT_ASSERT_VALUES_EQUAL(InverseMaskLowerBits(i), ~MaskLowerBits(i)); + UNIT_ASSERT_VALUES_EQUAL(MaskLowerBits(i, i / 2), (ui64{1} << i) - 1 << (i / 2)); + UNIT_ASSERT_VALUES_EQUAL(InverseMaskLowerBits(i, i / 2), ~MaskLowerBits(i, i / 2)); + } + } + + Y_UNIT_TEST(TestMostSignificantBit) { + static_assert(MostSignificantBitCT(0) == 0, "."); + static_assert(MostSignificantBitCT(1) == 0, "."); + static_assert(MostSignificantBitCT(5) == 2, "."); + + for (ui32 i = 0; i < 64; ++i) { + UNIT_ASSERT_VALUES_EQUAL(i, MostSignificantBit(ui64{1} << i)); + } + + for (ui32 i = 0; i < 63; ++i) { + UNIT_ASSERT_VALUES_EQUAL(i + 1, MostSignificantBit(ui64{3} << i)); + } + } + + Y_UNIT_TEST(TestLeastSignificantBit) { + for (ui32 i = 0; i < 64; ++i) { + UNIT_ASSERT_VALUES_EQUAL(i, LeastSignificantBit(ui64{1} << i)); + } + + for (ui32 i = 0; i < 63; ++i) { + UNIT_ASSERT_VALUES_EQUAL(i, LeastSignificantBit(ui64{3} << i)); + } + + for (ui32 i = 0; i < 64; ++i) { + ui64 value = (ui64(-1)) << i; + UNIT_ASSERT_VALUES_EQUAL(i, LeastSignificantBit(value)); + } + } + + Y_UNIT_TEST(TestCeilLog2) { + UNIT_ASSERT_VALUES_EQUAL(CeilLog2(ui64{1}), 1); + + for (ui32 i = 2; i < 64; ++i) { + UNIT_ASSERT_VALUES_EQUAL(CeilLog2(ui64{1} << i), i); + UNIT_ASSERT_VALUES_EQUAL(CeilLog2((ui64{1} << i) | ui64{1}), i + 1); + } + } + + Y_UNIT_TEST(TestReverse) { + for (ui64 i = 0; i < 0x100; ++i) { + UNIT_ASSERT_VALUES_EQUAL(ReverseBits((ui8)i), ReverseBitsSlow((ui8)i)); + UNIT_ASSERT_VALUES_EQUAL(ReverseBits((ui16)i), ReverseBitsSlow((ui16)i)); + UNIT_ASSERT_VALUES_EQUAL(ReverseBits((ui32)i), ReverseBitsSlow((ui32)i)); + UNIT_ASSERT_VALUES_EQUAL(ReverseBits((ui64)i), ReverseBitsSlow((ui64)i)); + UNIT_ASSERT_VALUES_EQUAL(ReverseBits((ui16)~i), ReverseBitsSlow((ui16)~i)); + UNIT_ASSERT_VALUES_EQUAL(ReverseBits((ui32)~i), ReverseBitsSlow((ui32)~i)); + UNIT_ASSERT_VALUES_EQUAL(ReverseBits((ui64)~i), ReverseBitsSlow((ui64)~i)); + } + + ui32 v = 0xF0F0F0F0; // 11110000111100001111000011110000 + for (ui32 i = 0; i < 4; ++i) { + UNIT_ASSERT_VALUES_EQUAL(ReverseBits(v, i + 1), v); + UNIT_ASSERT_VALUES_EQUAL(ReverseBits(v, 4 + 2 * i, 4 - i), v); + } + + UNIT_ASSERT_VALUES_EQUAL(ReverseBits(v, 8), 0xF0F0F00Fu); + UNIT_ASSERT_VALUES_EQUAL(ReverseBits(v, 8, 4), 0xF0F0FF00u); + + for (ui32 i = 0; i < 0x10000; ++i) { + for (ui32 j = 0; j <= 32; ++j) { + UNIT_ASSERT_VALUES_EQUAL_C(i, ReverseBits(ReverseBits(i, j), j), (TString)(TStringBuilder() << i << " " << j)); + } + } + } + + Y_UNIT_TEST(TestRotateBitsLeft) { + static_assert(RotateBitsLeftCT(0b00000000u, 0) == 0b00000000u, ""); + static_assert(RotateBitsLeftCT(0b00000001u, 0) == 0b00000001u, ""); + static_assert(RotateBitsLeftCT(0b10000000u, 0) == 0b10000000u, ""); + static_assert(RotateBitsLeftCT(0b00000001u, 1) == 0b00000010u, ""); + static_assert(RotateBitsLeftCT(0b10000000u, 1) == 0b00000001u, ""); + static_assert(RotateBitsLeftCT(0b00000101u, 1) == 0b00001010u, ""); + static_assert(RotateBitsLeftCT(0b10100000u, 1) == 0b01000001u, ""); + static_assert(RotateBitsLeftCT(0b10000000u, 7) == 0b01000000u, ""); + + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b00000000u, 0), 0b00000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b00000001u, 0), 0b00000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b10000000u, 0), 0b10000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b00000001u, 1), 0b00000010u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b10000000u, 1), 0b00000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b00000101u, 1), 0b00001010u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b10100000u, 1), 0b01000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b10000000u, 7), 0b01000000u); + + static_assert(RotateBitsLeftCT(0b0000000000000000u, 0) == 0b0000000000000000u, ""); + static_assert(RotateBitsLeftCT(0b0000000000000001u, 0) == 0b0000000000000001u, ""); + static_assert(RotateBitsLeftCT(0b1000000000000000u, 0) == 0b1000000000000000u, ""); + static_assert(RotateBitsLeftCT(0b0000000000000001u, 1) == 0b0000000000000010u, ""); + static_assert(RotateBitsLeftCT(0b1000000000000000u, 1) == 0b0000000000000001u, ""); + static_assert(RotateBitsLeftCT(0b0000000000000101u, 1) == 0b0000000000001010u, ""); + static_assert(RotateBitsLeftCT(0b1010000000000000u, 1) == 0b0100000000000001u, ""); + static_assert(RotateBitsLeftCT(0b1000000000000000u, 15) == 0b0100000000000000u, ""); + + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b0000000000000000u, 0), 0b0000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b0000000000000001u, 0), 0b0000000000000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b1000000000000000u, 0), 0b1000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b0000000000000001u, 1), 0b0000000000000010u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b1000000000000000u, 1), 0b0000000000000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b0000000000000101u, 1), 0b0000000000001010u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b1010000000000000u, 1), 0b0100000000000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b1000000000000000u, 15), 0b0100000000000000u); + + static_assert(RotateBitsLeftCT(0b00000000000000000000000000000000u, 0) == 0b00000000000000000000000000000000u, ""); + static_assert(RotateBitsLeftCT(0b00000000000000000000000000000001u, 0) == 0b00000000000000000000000000000001u, ""); + static_assert(RotateBitsLeftCT(0b10000000000000000000000000000000u, 0) == 0b10000000000000000000000000000000u, ""); + static_assert(RotateBitsLeftCT(0b00000000000000000000000000000001u, 1) == 0b00000000000000000000000000000010u, ""); + static_assert(RotateBitsLeftCT(0b10000000000000000000000000000000u, 1) == 0b00000000000000000000000000000001u, ""); + static_assert(RotateBitsLeftCT(0b00000000000000000000000000000101u, 1) == 0b00000000000000000000000000001010u, ""); + static_assert(RotateBitsLeftCT(0b10100000000000000000000000000000u, 1) == 0b01000000000000000000000000000001u, ""); + static_assert(RotateBitsLeftCT(0b10000000000000000000000000000000u, 31) == 0b01000000000000000000000000000000u, ""); + + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b00000000000000000000000000000000u, 0), 0b00000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b00000000000000000000000000000001u, 0), 0b00000000000000000000000000000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b10000000000000000000000000000000u, 0), 0b10000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b00000000000000000000000000000001u, 1), 0b00000000000000000000000000000010u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b10000000000000000000000000000000u, 1), 0b00000000000000000000000000000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b00000000000000000000000000000101u, 1), 0b00000000000000000000000000001010u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b10100000000000000000000000000000u, 1), 0b01000000000000000000000000000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b10000000000000000000000000000000u, 31), 0b01000000000000000000000000000000u); + + static_assert(RotateBitsLeftCT(0b0000000000000000000000000000000000000000000000000000000000000000u, 0) == 0b0000000000000000000000000000000000000000000000000000000000000000u, ""); + static_assert(RotateBitsLeftCT(0b0000000000000000000000000000000000000000000000000000000000000001u, 0) == 0b0000000000000000000000000000000000000000000000000000000000000001u, ""); + static_assert(RotateBitsLeftCT(0b1000000000000000000000000000000000000000000000000000000000000000u, 0) == 0b1000000000000000000000000000000000000000000000000000000000000000u, ""); + static_assert(RotateBitsLeftCT(0b0000000000000000000000000000000000000000000000000000000000000001u, 1) == 0b0000000000000000000000000000000000000000000000000000000000000010u, ""); + static_assert(RotateBitsLeftCT(0b1000000000000000000000000000000000000000000000000000000000000000u, 1) == 0b0000000000000000000000000000000000000000000000000000000000000001u, ""); + static_assert(RotateBitsLeftCT(0b0000000000000000000000000000000000000000000000000000000000000101u, 1) == 0b0000000000000000000000000000000000000000000000000000000000001010u, ""); + static_assert(RotateBitsLeftCT(0b1010000000000000000000000000000000000000000000000000000000000000u, 1) == 0b0100000000000000000000000000000000000000000000000000000000000001u, ""); + static_assert(RotateBitsLeftCT(0b1000000000000000000000000000000000000000000000000000000000000000u, 63) == 0b0100000000000000000000000000000000000000000000000000000000000000u, ""); + + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b0000000000000000000000000000000000000000000000000000000000000000u, 0), 0b0000000000000000000000000000000000000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b0000000000000000000000000000000000000000000000000000000000000001u, 0), 0b0000000000000000000000000000000000000000000000000000000000000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b1000000000000000000000000000000000000000000000000000000000000000u, 0), 0b1000000000000000000000000000000000000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b0000000000000000000000000000000000000000000000000000000000000001u, 1), 0b0000000000000000000000000000000000000000000000000000000000000010u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b1000000000000000000000000000000000000000000000000000000000000000u, 1), 0b0000000000000000000000000000000000000000000000000000000000000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b0000000000000000000000000000000000000000000000000000000000000101u, 1), 0b0000000000000000000000000000000000000000000000000000000000001010u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b1010000000000000000000000000000000000000000000000000000000000000u, 1), 0b0100000000000000000000000000000000000000000000000000000000000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsLeft(0b1000000000000000000000000000000000000000000000000000000000000000u, 63), 0b0100000000000000000000000000000000000000000000000000000000000000u); + } + + Y_UNIT_TEST(TestRotateBitsRight) { + static_assert(RotateBitsRightCT(0b00000000u, 0) == 0b00000000u, ""); + static_assert(RotateBitsRightCT(0b00000001u, 0) == 0b00000001u, ""); + static_assert(RotateBitsRightCT(0b10000000u, 0) == 0b10000000u, ""); + static_assert(RotateBitsRightCT(0b00000001u, 1) == 0b10000000u, ""); + static_assert(RotateBitsRightCT(0b10000000u, 1) == 0b01000000u, ""); + static_assert(RotateBitsRightCT(0b00000101u, 1) == 0b10000010u, ""); + static_assert(RotateBitsRightCT(0b10100000u, 1) == 0b01010000u, ""); + static_assert(RotateBitsRightCT(0b00000001u, 7) == 0b00000010u, ""); + + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b00000000u, 0), 0b00000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b00000001u, 0), 0b00000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b10000000u, 0), 0b10000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b00000001u, 1), 0b10000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b10000000u, 1), 0b01000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b00000101u, 1), 0b10000010u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b10100000u, 1), 0b01010000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b00000001u, 7), 0b00000010u); + + static_assert(RotateBitsRightCT(0b0000000000000000u, 0) == 0b0000000000000000u, ""); + static_assert(RotateBitsRightCT(0b0000000000000001u, 0) == 0b0000000000000001u, ""); + static_assert(RotateBitsRightCT(0b1000000000000000u, 0) == 0b1000000000000000u, ""); + static_assert(RotateBitsRightCT(0b0000000000000001u, 1) == 0b1000000000000000u, ""); + static_assert(RotateBitsRightCT(0b1000000000000000u, 1) == 0b0100000000000000u, ""); + static_assert(RotateBitsRightCT(0b0000000000000101u, 1) == 0b1000000000000010u, ""); + static_assert(RotateBitsRightCT(0b1010000000000000u, 1) == 0b0101000000000000u, ""); + static_assert(RotateBitsRightCT(0b0000000000000001u, 15) == 0b0000000000000010u, ""); + + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b0000000000000000u, 0), 0b0000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b0000000000000001u, 0), 0b0000000000000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b1000000000000000u, 0), 0b1000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b0000000000000001u, 1), 0b1000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b1000000000000000u, 1), 0b0100000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b0000000000000101u, 1), 0b1000000000000010u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b1010000000000000u, 1), 0b0101000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b0000000000000001u, 15), 0b0000000000000010u); + + static_assert(RotateBitsRightCT(0b00000000000000000000000000000000u, 0) == 0b00000000000000000000000000000000u, ""); + static_assert(RotateBitsRightCT(0b00000000000000000000000000000001u, 0) == 0b00000000000000000000000000000001u, ""); + static_assert(RotateBitsRightCT(0b10000000000000000000000000000000u, 0) == 0b10000000000000000000000000000000u, ""); + static_assert(RotateBitsRightCT(0b00000000000000000000000000000001u, 1) == 0b10000000000000000000000000000000u, ""); + static_assert(RotateBitsRightCT(0b10000000000000000000000000000000u, 1) == 0b01000000000000000000000000000000u, ""); + static_assert(RotateBitsRightCT(0b00000000000000000000000000000101u, 1) == 0b10000000000000000000000000000010u, ""); + static_assert(RotateBitsRightCT(0b10100000000000000000000000000000u, 1) == 0b01010000000000000000000000000000u, ""); + static_assert(RotateBitsRightCT(0b00000000000000000000000000000001u, 31) == 0b00000000000000000000000000000010u, ""); + + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b00000000000000000000000000000000u, 0), 0b00000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b00000000000000000000000000000001u, 0), 0b00000000000000000000000000000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b10000000000000000000000000000000u, 0), 0b10000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b00000000000000000000000000000001u, 1), 0b10000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b10000000000000000000000000000000u, 1), 0b01000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b00000000000000000000000000000101u, 1), 0b10000000000000000000000000000010u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b10100000000000000000000000000000u, 1), 0b01010000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b00000000000000000000000000000001u, 31), 0b00000000000000000000000000000010u); + + static_assert(RotateBitsRightCT(0b0000000000000000000000000000000000000000000000000000000000000000u, 0) == 0b0000000000000000000000000000000000000000000000000000000000000000u, ""); + static_assert(RotateBitsRightCT(0b0000000000000000000000000000000000000000000000000000000000000001u, 0) == 0b0000000000000000000000000000000000000000000000000000000000000001u, ""); + static_assert(RotateBitsRightCT(0b1000000000000000000000000000000000000000000000000000000000000000u, 0) == 0b1000000000000000000000000000000000000000000000000000000000000000u, ""); + static_assert(RotateBitsRightCT(0b0000000000000000000000000000000000000000000000000000000000000001u, 1) == 0b1000000000000000000000000000000000000000000000000000000000000000u, ""); + static_assert(RotateBitsRightCT(0b1000000000000000000000000000000000000000000000000000000000000000u, 1) == 0b0100000000000000000000000000000000000000000000000000000000000000u, ""); + static_assert(RotateBitsRightCT(0b0000000000000000000000000000000000000000000000000000000000000101u, 1) == 0b1000000000000000000000000000000000000000000000000000000000000010u, ""); + static_assert(RotateBitsRightCT(0b1010000000000000000000000000000000000000000000000000000000000000u, 1) == 0b0101000000000000000000000000000000000000000000000000000000000000u, ""); + static_assert(RotateBitsRightCT(0b0000000000000000000000000000000000000000000000000000000000000001u, 63) == 0b0000000000000000000000000000000000000000000000000000000000000010u, ""); + + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b0000000000000000000000000000000000000000000000000000000000000000u, 0), 0b0000000000000000000000000000000000000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b0000000000000000000000000000000000000000000000000000000000000001u, 0), 0b0000000000000000000000000000000000000000000000000000000000000001u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b1000000000000000000000000000000000000000000000000000000000000000u, 0), 0b1000000000000000000000000000000000000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b0000000000000000000000000000000000000000000000000000000000000001u, 1), 0b1000000000000000000000000000000000000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b1000000000000000000000000000000000000000000000000000000000000000u, 1), 0b0100000000000000000000000000000000000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b0000000000000000000000000000000000000000000000000000000000000101u, 1), 0b1000000000000000000000000000000000000000000000000000000000000010u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b1010000000000000000000000000000000000000000000000000000000000000u, 1), 0b0101000000000000000000000000000000000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(RotateBitsRight(0b0000000000000000000000000000000000000000000000000000000000000001u, 63), 0b0000000000000000000000000000000000000000000000000000000000000010u); + } + + Y_UNIT_TEST(TestSelectBits) { + ui8 firstui8Test = SelectBits<3, 4, ui8>(0b11111111u); + ui8 secondui8Test = SelectBits<2, 5, ui8>(0b11101101u); + UNIT_ASSERT_VALUES_EQUAL(firstui8Test, 0b00001111u); + UNIT_ASSERT_VALUES_EQUAL(secondui8Test, 0b00011011u); + + ui16 firstui16Test = SelectBits<9, 2, ui16>(0b1111111111111111u); + ui16 secondui16Test = SelectBits<3, 6, ui16>(0b1010011111010001u); + UNIT_ASSERT_VALUES_EQUAL(firstui16Test, 0b0000000000000011u); + UNIT_ASSERT_VALUES_EQUAL(secondui16Test, 0b0000000000111010u); + + ui32 firstui32Test = SelectBits<23, 31, ui32>(0b11111111111111111111111111111111u); + ui32 secondui32Test = SelectBits<0, 31, ui32>(0b10001011101010011111010000111111u); + UNIT_ASSERT_VALUES_EQUAL(firstui32Test, 0b00000000000000000000000111111111u); + UNIT_ASSERT_VALUES_EQUAL(secondui32Test, 0b00001011101010011111010000111111); + + ui64 firstui64Test = SelectBits<1, 62, ui64>(0b1111000000000000000000000000000000000000000000000000000000000000u); + ui64 secondui64Test = SelectBits<32, 43, ui64>(0b1111111111111111111111111111111111111111111111111111111111111111u); + UNIT_ASSERT_VALUES_EQUAL(firstui64Test, 0b0011100000000000000000000000000000000000000000000000000000000000u); + UNIT_ASSERT_VALUES_EQUAL(secondui64Test, 0b0000000000000000000000000000000011111111111111111111111111111111u); + } + + Y_UNIT_TEST(TestSetBits) { + ui8 firstui8Test = 0b11111111u; + SetBits<3, 4, ui8>(firstui8Test, 0b00001111u); + ui8 secondui8Test = 0b11101101u; + SetBits<2, 7, ui8>(secondui8Test, 0b01110111u); + UNIT_ASSERT_VALUES_EQUAL(firstui8Test, 0b11111111u); + UNIT_ASSERT_VALUES_EQUAL(secondui8Test, 0b11011101u); + + ui16 firstui16Test = 0b1111111111111111u; + SetBits<9, 4, ui16>(firstui16Test, 0b000000000000111u); + ui16 secondui16Test = 0b1010011111010001u; + SetBits<3, 15, ui16>(secondui16Test, 0b0010011111010001u); + UNIT_ASSERT_VALUES_EQUAL(firstui16Test, 0b1110111111111111u); + UNIT_ASSERT_VALUES_EQUAL(secondui16Test, 0b0011111010001001u); + + ui32 firstui32Test = 0b11111111111111111111111111111111u; + SetBits<23, 31, ui32>(firstui32Test, 0b01100001111111111001111101111111u); + ui32 secondui32Test = 0b10001011101010011111010000111111u; + SetBits<0, 31, ui32>(secondui32Test, 0b01111111111111111111111111111111u); + UNIT_ASSERT_VALUES_EQUAL(firstui32Test, 0b10111111111111111111111111111111u); + UNIT_ASSERT_VALUES_EQUAL(secondui32Test, 0b11111111111111111111111111111111u); + + ui64 firstui64Test = 0b1111000000000000000000000000000000000000000000000000000000000000u; + SetBits<1, 62, ui64>(firstui64Test, 0b0001000000000000000000000000000000000000000000000000000001010101u); + ui64 secondui64Test = 0b1111111111111111111111111111111111111111111111111111111111111111u; + SetBits<32, 43, ui64>(secondui64Test, 0b0000000000000000000000000000000000000111111111111111111111111111u); + UNIT_ASSERT_VALUES_EQUAL(firstui64Test, 0b1010000000000000000000000000000000000000000000000000000010101010u); + UNIT_ASSERT_VALUES_EQUAL(secondui64Test, 0b0000011111111111111111111111111111111111111111111111111111111111u); + } +} diff --git a/util/generic/bt_exception.cpp b/util/generic/bt_exception.cpp new file mode 100644 index 00000000000..609870c4c00 --- /dev/null +++ b/util/generic/bt_exception.cpp @@ -0,0 +1 @@ +#include "bt_exception.h" diff --git a/util/generic/bt_exception.h b/util/generic/bt_exception.h new file mode 100644 index 00000000000..018d2bc89a1 --- /dev/null +++ b/util/generic/bt_exception.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "yexception.h" + +#include + +template +class TWithBackTrace: public T { +public: + template + inline TWithBackTrace(Args&&... args) + : T(std::forward(args)...) + { + BT_.Capture(); + } + + const TBackTrace* BackTrace() const noexcept override { + return &BT_; + } + +private: + TBackTrace BT_; +}; diff --git a/util/generic/buffer.cpp b/util/generic/buffer.cpp new file mode 100644 index 00000000000..b92697e1d00 --- /dev/null +++ b/util/generic/buffer.cpp @@ -0,0 +1,99 @@ +#include "buffer.h" +#include "mem_copy.h" +#include "string.h" +#include "ymath.h" + +#include +#include + +TBuffer::TBuffer(size_t len) + : Data_(nullptr) + , Len_(0) + , Pos_(0) +{ + Reserve(len); +} + +TBuffer::TBuffer(TBuffer&& b) noexcept + : Data_(nullptr) + , Len_(0) + , Pos_(0) +{ + Swap(b); +} + +TBuffer::TBuffer(const char* buf, size_t len) + : Data_(nullptr) + , Len_(0) + , Pos_(0) +{ + Append(buf, len); +} + +TBuffer& TBuffer::operator=(TBuffer&& b) noexcept { + y_deallocate(Data_); + + Data_ = b.Data_; + Len_ = b.Len_; + Pos_ = b.Pos_; + + b.Data_ = nullptr; + b.Len_ = 0; + b.Pos_ = 0; + + return *this; +} + +void TBuffer::Append(const char* buf, size_t len) { + if (len > Avail()) { + Reserve(Pos_ + len); + } + + Y_ASSERT(len <= Avail()); + + MemCopy(Data() + Pos_, buf, len); + NSan::Unpoison(Data() + Pos_, len); + Pos_ += len; + + Y_ASSERT(Pos_ <= Len_); +} + +void TBuffer::Fill(char ch, size_t len) { + if (len > Avail()) { + Reserve(Pos_ + len); + } + + Y_ASSERT(len <= Avail()); + + memset(Data() + Pos_, ch, len); + NSan::Unpoison(Data() + Pos_, len); + Pos_ += len; + + Y_ASSERT(Pos_ <= Len_); +} + +void TBuffer::DoReserve(size_t realLen) { + // FastClp2(x) returns 0 on x from [Max/2 + 2, Max] + const size_t len = Max(FastClp2(realLen), realLen); + + Y_ASSERT(realLen > Len_); + Y_ASSERT(len >= realLen); + + Realloc(len); +} + +void TBuffer::Realloc(size_t len) { + Y_ASSERT(Pos_ <= len); + + Data_ = (char*)y_reallocate(Data_, len); + Len_ = len; +} + +TBuffer::~TBuffer() { + y_deallocate(Data_); +} + +void TBuffer::AsString(TString& s) { + s.assign(Data(), Size()); + Clear(); +} diff --git a/util/generic/buffer.h b/util/generic/buffer.h new file mode 100644 index 00000000000..a5efe398ce7 --- /dev/null +++ b/util/generic/buffer.h @@ -0,0 +1,246 @@ +#pragma once + +#include "utility.h" + +#include +#include +#include +#include + +#include + +class TBuffer { +public: + using TIterator = char*; + using TConstIterator = const char*; + + TBuffer(size_t len = 0); + TBuffer(const char* buf, size_t len); + + TBuffer(const TBuffer& b) + : Data_(nullptr) + , Len_(0) + , Pos_(0) + { + *this = b; + } + + TBuffer(TBuffer&& b) noexcept; + + TBuffer& operator=(TBuffer&& b) noexcept; + + TBuffer& operator=(const TBuffer& b) { + if (this != &b) { + Assign(b.Data(), b.Size()); + } + return *this; + } + + ~TBuffer(); + + inline void Clear() noexcept { + Pos_ = 0; + } + + inline void EraseBack(size_t n) noexcept { + Y_ASSERT(n <= Pos_); + Pos_ -= n; + } + + Y_REINITIALIZES_OBJECT inline void Reset() noexcept { + TBuffer().Swap(*this); + } + + inline void Assign(const char* data, size_t len) { + Clear(); + Append(data, len); + } + + inline void Assign(const char* b, const char* e) { + Assign(b, e - b); + } + + inline char* Data() noexcept { + return Data_; + } + + inline const char* Data() const noexcept { + return Data_; + } + + inline char* Pos() noexcept { + return Data_ + Pos_; + } + + inline const char* Pos() const noexcept { + return Data_ + Pos_; + } + + /// Used space in bytes (do not mix with Capacity!) + inline size_t Size() const noexcept { + return Pos_; + } + + Y_PURE_FUNCTION inline bool Empty() const noexcept { + return !Size(); + } + + inline explicit operator bool() const noexcept { + return Size(); + } + + inline size_t Avail() const noexcept { + return Len_ - Pos_; + } + + void Append(const char* buf, size_t len); + + inline void Append(const char* b, const char* e) { + Append(b, e - b); + } + + inline void Append(char ch) { + if (Len_ == Pos_) { + Reserve(Len_ + 1); + } + + *(Data() + Pos_++) = ch; + } + + void Fill(char ch, size_t len); + + // Method is useful when first messages from buffer are processed, and + // the last message in buffer is incomplete, so we need to move partial + // message to the begin of the buffer and continue filling the buffer + // from the network. + inline void Chop(size_t pos, size_t count) { + const auto end = pos + count; + Y_ASSERT(end <= Pos_); + + if (count == 0) { + return; + } else if (count == Pos_) { + Pos_ = 0; + } else { + memmove(Data_ + pos, Data_ + end, Pos_ - end); + Pos_ -= count; + } + } + + inline void ChopHead(size_t count) { + Chop(0U, count); + } + + inline void Proceed(size_t pos) { + //Y_ASSERT(pos <= Len_); // see discussion in REVIEW:29021 + Resize(pos); + } + + inline void Advance(size_t len) { + Resize(Pos_ + len); + } + + inline void Reserve(size_t len) { + if (len > Len_) { + DoReserve(len); + } + } + + inline void ReserveExactNeverCallMeInSaneCode(size_t len) { + if (len > Len_) { + Realloc(len); + } + } + + inline void ShrinkToFit() { + if (Pos_ < Len_) { + Realloc(Pos_); + } + } + + inline void Resize(size_t len) { + Reserve(len); + Pos_ = len; + } + + // Method works like Resize, but allocates exact specified number of bytes + // rather than rounded up to next power of 2 + // Use with care + inline void ResizeExactNeverCallMeInSaneCode(size_t len) { + ReserveExactNeverCallMeInSaneCode(len); + Pos_ = len; + } + + inline size_t Capacity() const noexcept { + return Len_; + } + + inline void AlignUp(size_t align, char fillChar = '\0') { + size_t diff = ::AlignUp(Pos_, align) - Pos_; + while (diff-- > 0) { + Append(fillChar); + } + } + + inline char* data() noexcept { + return Data(); + } + + inline const char* data() const noexcept { + return Data(); + } + + inline size_t size() const noexcept { + return Size(); + } + + inline void Swap(TBuffer& r) noexcept { + DoSwap(Data_, r.Data_); + DoSwap(Len_, r.Len_); + DoSwap(Pos_, r.Pos_); + } + + /* + * after this call buffer becomes empty + */ + void AsString(TString& s); + + /* + * iterator-like interface + */ + inline TIterator Begin() noexcept { + return Data(); + } + + inline TIterator End() noexcept { + return Begin() + Size(); + } + + inline TConstIterator Begin() const noexcept { + return Data(); + } + + inline TConstIterator End() const noexcept { + return Begin() + Size(); + } + + bool operator==(const TBuffer& other) const noexcept { + if (Empty()) { + return other.Empty(); + } + return Size() == other.Size() && 0 == std::memcmp(Data(), other.Data(), Size()); + } + + bool operator!=(const TBuffer& other) const noexcept { + return !(*this == other); + } + +private: + void DoReserve(size_t len); + void Realloc(size_t len); + +private: + char* Data_; + size_t Len_; + size_t Pos_; +}; diff --git a/util/generic/buffer_ut.cpp b/util/generic/buffer_ut.cpp new file mode 100644 index 00000000000..437d7122ec2 --- /dev/null +++ b/util/generic/buffer_ut.cpp @@ -0,0 +1,205 @@ +#include + +#include +#include "string.h" +#include "vector.h" +#include "buffer.h" + +Y_UNIT_TEST_SUITE(TBufferTest) { + Y_UNIT_TEST(TestEraseBack) { + TBuffer buf; + + buf.Append("1234567", 7); + buf.Reserve(1000); + buf.Resize(6); + buf.EraseBack(2); + + UNIT_ASSERT_EQUAL(TString(buf.data(), buf.size()), "1234"); + } + + Y_UNIT_TEST(TestAppend) { + const char data[] = "1234567890qwertyuiop"; + + TBuffer buf(13); + TString str; + + for (size_t i = 0; i < 10; ++i) { + for (size_t j = 0; j < sizeof(data) - 1; ++j) { + buf.Append(data, j); + buf.Append('q'); + str.append(data, j); + str.append('q'); + } + } + + UNIT_ASSERT_EQUAL(TString(buf.data(), buf.size()), str); + } + + Y_UNIT_TEST(TestReset) { + char content[] = "some text"; + TBuffer buf; + + buf.Append(content, sizeof(content)); + buf.Clear(); + + UNIT_ASSERT(buf.Capacity() != 0); + + buf.Append(content, sizeof(content)); + buf.Reset(); + + UNIT_ASSERT_EQUAL(buf.Capacity(), 0); + } + + Y_UNIT_TEST(TestResize) { + char content[] = "some text"; + TBuffer buf; + + buf.Resize(10); + UNIT_ASSERT_VALUES_EQUAL(buf.size(), 10u); + + buf.Resize(0); + UNIT_ASSERT_VALUES_EQUAL(buf.size(), 0u); + + buf.Resize(9); + memcpy(buf.data(), content, 9); + UNIT_ASSERT_VALUES_EQUAL(TString(buf.data(), buf.size()), "some text"); + + buf.Resize(4); + UNIT_ASSERT_VALUES_EQUAL(TString(buf.data(), buf.size()), "some"); + } + + Y_UNIT_TEST(TestReserve) { + TBuffer buf; + UNIT_ASSERT_EQUAL(buf.Capacity(), 0); + + buf.Reserve(4); + UNIT_ASSERT_EQUAL(buf.Capacity(), 4); + + buf.Reserve(6); + UNIT_ASSERT_EQUAL(buf.Capacity(), 8); + + buf.Reserve(32); + UNIT_ASSERT_EQUAL(buf.Capacity(), 32); + + buf.Reserve(33); + UNIT_ASSERT_EQUAL(buf.Capacity(), 64); + buf.Reserve(64); + UNIT_ASSERT_EQUAL(buf.Capacity(), 64); + + buf.Resize(128); + UNIT_ASSERT_EQUAL(buf.Capacity(), 128); + + buf.Append('a'); + UNIT_ASSERT_EQUAL(buf.Capacity(), 256); + TString tmp1 = "abcdef"; + buf.Append(tmp1.data(), tmp1.size()); + UNIT_ASSERT_EQUAL(buf.Capacity(), 256); + + TString tmp2 = "30498290sfokdsflj2308w"; + buf.Resize(1020); + buf.Append(tmp2.data(), tmp2.size()); + UNIT_ASSERT_EQUAL(buf.Capacity(), 2048); + } + + Y_UNIT_TEST(TestShrinkToFit) { + TBuffer buf; + + TString content = "some text"; + buf.Append(content.data(), content.size()); + UNIT_ASSERT_EQUAL(buf.Size(), 9); + UNIT_ASSERT_EQUAL(buf.Capacity(), 16); + + buf.ShrinkToFit(); + UNIT_ASSERT_EQUAL(buf.Size(), 9); + UNIT_ASSERT_EQUAL(buf.Capacity(), 9); + UNIT_ASSERT_EQUAL(TString(buf.data(), buf.size()), content); + + const size_t MB = 1024 * 1024; + buf.Resize(MB); + UNIT_ASSERT_EQUAL(buf.Capacity(), MB); + buf.ShrinkToFit(); + UNIT_ASSERT_EQUAL(buf.Capacity(), MB); + buf.Resize(MB + 100); + UNIT_ASSERT_EQUAL(buf.Capacity(), 2 * MB); + buf.ShrinkToFit(); + UNIT_ASSERT_EQUAL(buf.Capacity(), MB + 100); + } + +#if 0 +Y_UNIT_TEST(TestAlignUp) { + char content[] = "some text"; + TBuffer buf; + + buf.Append(content, sizeof(content)); + buf.AlignUp(4, '!'); + + UNIT_ASSERT(buf.Size() % 4 == 0); + UNIT_ASSERT_VALUES_EQUAL(TString(~buf, +buf), "some text!!!"); + + char addContent[] = "1234"; + buf.Append(addContent, sizeof(addContent)); + buf.AlignUp(4, 'X'); + UNIT_ASSERT(buf.Size() % 4 == 0); + UNIT_ASSERT_VALUES_EQUAL(TString(~buf, +buf), "some text!!!1234"); +} +#endif + +#if 0 +Y_UNIT_TEST(TestSpeed) { + const char data[] = "1234567890qwertyuiop"; + const size_t c = 100000; + ui64 t1 = 0; + ui64 t2 = 0; + + { + TBuffer buf; + + t1 = MicroSeconds(); + + for (size_t i = 0; i < c; ++i) { + buf.Append(data, sizeof(data)); + } + + t1 = MicroSeconds() - t1; + } + + { + TVector buf; + + t2 = MicroSeconds(); + + for (size_t i = 0; i < c; ++i) { + buf.insert(buf.end(), data, data + sizeof(data)); + } + + t2 = MicroSeconds() - t2; + } + + UNIT_ASSERT(t1 < t2); +} +#endif + + Y_UNIT_TEST(TestFillAndChop) { + TBuffer buf; + buf.Append("Some ", 5); + buf.Fill('!', 5); + buf.Append(" text.", 6); + UNIT_ASSERT_VALUES_EQUAL(TString(buf.data(), buf.size()), "Some !!!!! text."); + + buf.Chop(5, 6); + UNIT_ASSERT_VALUES_EQUAL(TString(buf.data(), buf.size()), "Some text."); + } + + Y_UNIT_TEST(TestComparison) { + TBuffer buf1("abcd", 4); + TBuffer buf2("abcde", 5); + TBuffer empty; + UNIT_ASSERT(empty == empty); + UNIT_ASSERT(!(empty != empty)); + UNIT_ASSERT(buf1 != buf2); + UNIT_ASSERT(buf1 == buf1); + buf2.EraseBack(1); + UNIT_ASSERT(buf2 == buf1); + } + +} diff --git a/util/generic/cast.cpp b/util/generic/cast.cpp new file mode 100644 index 00000000000..11f82364d39 --- /dev/null +++ b/util/generic/cast.cpp @@ -0,0 +1 @@ +#include "cast.h" diff --git a/util/generic/cast.h b/util/generic/cast.h new file mode 100644 index 00000000000..f827bde5cb4 --- /dev/null +++ b/util/generic/cast.h @@ -0,0 +1,176 @@ +#pragma once + +#include "typetraits.h" +#include "yexception.h" + +#include +#include +#include +#include + +#include + +template +static inline T VerifyDynamicCast(F f) { + if (!f) { + return nullptr; + } + + T ret = dynamic_cast(f); + + Y_ABORT_UNLESS(ret, "verify cast failed"); + + return ret; +} + +#if !defined(NDEBUG) + #define USE_DEBUG_CHECKED_CAST +#endif + +namespace NPrivate { + template + static T DynamicCast(F f) { + return dynamic_cast(f); + } +} + +/* + * replacement for dynamic_cast(dynamic_cast in debug mode, else static_cast) + */ +template +static inline T CheckedCast(F f) { +#if defined(USE_DEBUG_CHECKED_CAST) + return VerifyDynamicCast(f); +#else + /* Make sure F is polymorphic. + * Without this cast, CheckedCast with non-polymorphic F + * incorrectly compiled without error in release mode. + */ + { + auto&& x = &::NPrivate::DynamicCast; + + (void)x; + } + + return static_cast(f); +#endif // USE_DEBUG_CHECKED_CAST +} + +/* + * be polite + */ +#undef USE_DEBUG_CHECKED_CAST + +template +class TInteger; + +template <> +class TInteger { +public: + template + static constexpr bool IsNegative(TUnsigned) noexcept { + return false; + } +}; + +template <> +class TInteger { +public: + template + static constexpr bool IsNegative(const TSigned value) noexcept { + return value < 0; + } +}; + +template +constexpr bool IsNegative(const TType value) noexcept { + return TInteger::value>::IsNegative(value); +} + +namespace NPrivate { + template + using TUnderlyingTypeOrSelf = typename std::conditional< + std::is_enum::value, + std::underlying_type, // Lazy evaluatuion: do not call ::type here, because underlying_type is undefined if T is not an enum. + std::enable_if // Wrapping T in a class, that has member ::type typedef. + >::type::type; // Left ::type is for std::conditional, right ::type is for underlying_type/enable_if + + template + struct TSafelyConvertible { + using TSmallInt = TUnderlyingTypeOrSelf; + using TLargeInt = TUnderlyingTypeOrSelf; + + static constexpr bool Result = std::is_integral::value && std::is_integral::value && + ((std::is_signed::value == std::is_signed::value && sizeof(TSmallInt) >= sizeof(TLargeInt)) || + (std::is_signed::value && sizeof(TSmallInt) > sizeof(TLargeInt))); + }; +} + +template +constexpr std::enable_if_t<::NPrivate::TSafelyConvertible::Result, TSmallInt> SafeIntegerCast(TLargeInt largeInt) noexcept { + return static_cast(largeInt); +} + +template +inline std::enable_if_t::Result, TSmall> SafeIntegerCast(TLarge largeInt) { + using TSmallInt = ::NPrivate::TUnderlyingTypeOrSelf; + using TLargeInt = ::NPrivate::TUnderlyingTypeOrSelf; + + if (std::is_unsigned::value && std::is_signed::value) { + if (IsNegative(largeInt)) { + ythrow TBadCastException() << "Conversion '" << TypeName() << '{' << TLargeInt(largeInt) << "}' to '" + << TypeName() + << "', negative value converted to unsigned"; + } + } + + TSmallInt smallInt = TSmallInt(largeInt); + + if (std::is_signed::value && std::is_unsigned::value) { + if (IsNegative(smallInt)) { + ythrow TBadCastException() << "Conversion '" << TypeName() << '{' << TLargeInt(largeInt) << "}' to '" + << TypeName() + << "', positive value converted to negative"; + } + } + + if (TLargeInt(smallInt) != largeInt) { + ythrow TBadCastException() << "Conversion '" << TypeName() << '{' << TLargeInt(largeInt) << "}' to '" + << TypeName() << "', loss of data"; + } + + return static_cast(smallInt); +} + +template +inline TSmallInt IntegerCast(TLargeInt largeInt) noexcept { + try { + return SafeIntegerCast(largeInt); + } catch (const yexception& exc) { + Y_ABORT("IntegerCast: %s", exc.what()); + } +} + +/* Convert given enum value to its underlying type. This is just a shortcut for + * `static_cast>(enum_)`. + */ +template +constexpr std::underlying_type_t ToUnderlying(const T enum_) noexcept { + return static_cast>(enum_); +} + +// std::bit_cast from c++20 +template +TTarget BitCast(const TSource& source) { + static_assert(sizeof(TSource) == sizeof(TTarget), "Size mismatch"); + static_assert(std::is_trivially_copyable::value, "TSource is not trivially copyable"); + static_assert(std::is_trivial::value, "TTarget is not trivial"); + + // Support volatile qualifiers. + // ReadUnaligned does not work with volatile pointers, so cast away + // volatileness beforehand. + using TNonvolatileSource = std::remove_volatile_t; + using TNonvolatileTarget = std::remove_volatile_t; + + return ReadUnaligned(&const_cast(source)); +} diff --git a/util/generic/cast_ut.cpp b/util/generic/cast_ut.cpp new file mode 100644 index 00000000000..718a8de79de --- /dev/null +++ b/util/generic/cast_ut.cpp @@ -0,0 +1,112 @@ +#include "cast.h" + +#include + +class TGenericCastsTest: public TTestBase { + UNIT_TEST_SUITE(TGenericCastsTest); + UNIT_TEST(TestVerifyDynamicCast) + UNIT_TEST(TestIntegralCast) + UNIT_TEST(TestEnumCast) + UNIT_TEST(TestToUnderlying) + UNIT_TEST(TestBitCast) + UNIT_TEST_SUITE_END(); + +private: + struct TAaa { + virtual ~TAaa() = default; + }; + struct TBbb: public TAaa {}; + + inline void TestVerifyDynamicCast() { + TBbb bbb; + TAaa* aaa = &bbb; + TAaa* aaa2 = VerifyDynamicCast(aaa); + UNIT_ASSERT(aaa == aaa2); + } + + void TestIntegralCast() { + UNIT_ASSERT_EXCEPTION(SafeIntegerCast(-5), TBadCastException); + UNIT_ASSERT_EXCEPTION(SafeIntegerCast(static_cast(Max() + 10)), TBadCastException); + UNIT_ASSERT_EXCEPTION(SafeIntegerCast(static_cast(Max() + 10)), TBadCastException); + } + + inline void TestEnumCast() { + enum A { + AM1 = -1 + }; + + enum B: int { + BM1 = -1 + }; + + enum class C: unsigned short { + CM1 = 1 + }; + + UNIT_ASSERT_EXCEPTION(SafeIntegerCast(AM1), TBadCastException); + UNIT_ASSERT_EXCEPTION(SafeIntegerCast(BM1), TBadCastException); + UNIT_ASSERT_EXCEPTION(SafeIntegerCast(AM1), TBadCastException); + UNIT_ASSERT_EXCEPTION(static_cast(SafeIntegerCast(BM1)), TBadCastException); + UNIT_ASSERT(SafeIntegerCast
(BM1) == AM1); + UNIT_ASSERT(SafeIntegerCast(AM1) == BM1); + UNIT_ASSERT(SafeIntegerCast(C::CM1) == 1); + UNIT_ASSERT(SafeIntegerCast(C::CM1) == 1); + UNIT_ASSERT(SafeIntegerCast(-1) == AM1); + UNIT_ASSERT(SafeIntegerCast(-1) == BM1); + UNIT_ASSERT(SafeIntegerCast(1) == C::CM1); + } + + void TestToUnderlying() { + enum A { + AM1 = -1 + }; + + enum B: int { + BM1 = -1 + }; + + enum class C: unsigned short { + CM1 = 1 + }; + + static_assert(static_cast>(AM1) == ToUnderlying(AM1), ""); + static_assert(static_cast>(BM1) == ToUnderlying(BM1), ""); + static_assert(static_cast>(C::CM1) == ToUnderlying(C::CM1), ""); + + static_assert(std::is_same, decltype(ToUnderlying(AM1))>::value, ""); + static_assert(std::is_same, decltype(ToUnderlying(BM1))>::value, ""); + static_assert(std::is_same, decltype(ToUnderlying(C::CM1))>::value, ""); + + UNIT_ASSERT_VALUES_EQUAL(static_cast>(AM1), ToUnderlying(AM1)); + UNIT_ASSERT_VALUES_EQUAL(static_cast>(BM1), ToUnderlying(BM1)); + UNIT_ASSERT_VALUES_EQUAL(static_cast>(C::CM1), ToUnderlying(C::CM1)); + } + + void TestBitCast() { + // Change sign of float + { + const float floatValue = 17.33f; + ui32 ui32Value = BitCast(floatValue); + ui32Value ^= (ui32)1 << 31; + UNIT_ASSERT_VALUES_EQUAL(-floatValue, BitCast(ui32Value)); + } + + // Unpack ui64 into a struct + { + const ui64 value = 0x1122334455667788; + struct TStruct { + ui32 a; + ui16 b; + ui8 c; + ui8 d; + }; + auto structValue = BitCast(value); + UNIT_ASSERT_VALUES_EQUAL(structValue.a, 0x55667788); + UNIT_ASSERT_VALUES_EQUAL(structValue.b, 0x3344); + UNIT_ASSERT_VALUES_EQUAL(structValue.c, 0x22); + UNIT_ASSERT_VALUES_EQUAL(structValue.d, 0x11); + } + } +}; + +UNIT_TEST_SUITE_REGISTRATION(TGenericCastsTest); diff --git a/util/generic/deque.cpp b/util/generic/deque.cpp new file mode 100644 index 00000000000..3c055780ab3 --- /dev/null +++ b/util/generic/deque.cpp @@ -0,0 +1 @@ +#include "deque.h" diff --git a/util/generic/deque.h b/util/generic/deque.h new file mode 100644 index 00000000000..2dabaf3177d --- /dev/null +++ b/util/generic/deque.h @@ -0,0 +1,25 @@ +#pragma once + +#include "fwd.h" + +#include + +#include +#include +#include + +template +class TDeque: public std::deque> { + using TBase = std::deque>; + +public: + using TBase::TBase; + + inline yssize_t ysize() const noexcept { + return (yssize_t)this->size(); + } + + inline explicit operator bool() const noexcept { + return !this->empty(); + } +}; diff --git a/util/generic/deque.pxd b/util/generic/deque.pxd new file mode 100644 index 00000000000..62834ac2ad8 --- /dev/null +++ b/util/generic/deque.pxd @@ -0,0 +1,9 @@ +from libcpp.deque cimport deque + + +cdef extern from "" nogil: + cdef cppclass TDeque[T](deque): + TDeque() except + + TDeque(size_t) except + + TDeque(size_t, const T&) except + + TDeque(const TDeque&) except + diff --git a/util/generic/deque_ut.cpp b/util/generic/deque_ut.cpp new file mode 100644 index 00000000000..38c421c7c79 --- /dev/null +++ b/util/generic/deque_ut.cpp @@ -0,0 +1,245 @@ +#include "deque.h" + +#include + +#include +#include "yexception.h" + +class TDequeTest: public TTestBase { + UNIT_TEST_SUITE(TDequeTest); + UNIT_TEST(TestConstructorsAndAssignments); + UNIT_TEST(TestDeque1); + UNIT_TEST(TestAt); + UNIT_TEST(TestInsert); + UNIT_TEST(TestErase); + UNIT_TEST(TestAutoRef); + UNIT_TEST_SUITE_END(); + +protected: + void TestConstructorsAndAssignments(); + void TestDeque1(); + void TestInsert(); + void TestErase(); + void TestAt(); + void TestAutoRef(); +}; + +UNIT_TEST_SUITE_REGISTRATION(TDequeTest); + +void TDequeTest::TestConstructorsAndAssignments() { + using container = TDeque; + + container c1; + c1.push_back(100); + c1.push_back(200); + + container c2(c1); + + UNIT_ASSERT_VALUES_EQUAL(2, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(100, c1.at(0)); + UNIT_ASSERT_VALUES_EQUAL(200, c2.at(1)); + + container c3(std::move(c1)); + + UNIT_ASSERT_VALUES_EQUAL(0, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c3.size()); + UNIT_ASSERT_VALUES_EQUAL(100, c3.at(0)); + + c2.push_back(300); + c3 = c2; + + UNIT_ASSERT_VALUES_EQUAL(3, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(3, c3.size()); + UNIT_ASSERT_VALUES_EQUAL(300, c3.at(2)); + + c2.push_back(400); + c3 = std::move(c2); + + UNIT_ASSERT_VALUES_EQUAL(0, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(4, c3.size()); + UNIT_ASSERT_VALUES_EQUAL(400, c3.at(3)); + + int array[] = {2, 3, 4}; + container c4 = {2, 3, 4}; + UNIT_ASSERT_VALUES_EQUAL(c4, container(std::begin(array), std::end(array))); +} + +void TDequeTest::TestDeque1() { + TDeque d; + UNIT_ASSERT(!d); + + d.push_back(4); + d.push_back(9); + d.push_back(16); + d.push_front(1); + + UNIT_ASSERT(d); + + UNIT_ASSERT(d[0] == 1); + UNIT_ASSERT(d[1] == 4); + UNIT_ASSERT(d[2] == 9); + UNIT_ASSERT(d[3] == 16); + + d.pop_front(); + d[2] = 25; + + UNIT_ASSERT(d[0] == 4); + UNIT_ASSERT(d[1] == 9); + UNIT_ASSERT(d[2] == 25); + + //Some compile time tests: + TDeque::iterator dit = d.begin(); + TDeque::const_iterator cdit(d.begin()); + + UNIT_ASSERT((dit - cdit) == 0); + UNIT_ASSERT((cdit - dit) == 0); + UNIT_ASSERT((dit - dit) == 0); + UNIT_ASSERT((cdit - cdit) == 0); + UNIT_ASSERT(!((dit < cdit) || (dit > cdit) || (dit != cdit) || !(dit <= cdit) || !(dit >= cdit))); +} + +void TDequeTest::TestInsert() { + TDeque d; + d.push_back(0); + d.push_back(1); + d.push_back(2); + + UNIT_ASSERT(d.size() == 3); + + TDeque::iterator dit; + + //Insertion before begin: + dit = d.insert(d.begin(), 3); + UNIT_ASSERT(dit != d.end()); + UNIT_ASSERT(*dit == 3); + UNIT_ASSERT(d.size() == 4); + UNIT_ASSERT(d[0] == 3); + + //Insertion after begin: + dit = d.insert(d.begin() + 1, 4); + UNIT_ASSERT(dit != d.end()); + UNIT_ASSERT(*dit == 4); + UNIT_ASSERT(d.size() == 5); + UNIT_ASSERT(d[1] == 4); + + //Insertion at end: + dit = d.insert(d.end(), 5); + UNIT_ASSERT(dit != d.end()); + UNIT_ASSERT(*dit == 5); + UNIT_ASSERT(d.size() == 6); + UNIT_ASSERT(d[5] == 5); + + //Insertion before last element: + dit = d.insert(d.end() - 1, 6); + UNIT_ASSERT(dit != d.end()); + UNIT_ASSERT(*dit == 6); + UNIT_ASSERT(d.size() == 7); + UNIT_ASSERT(d[5] == 6); + + //Insertion of several elements before begin + d.insert(d.begin(), 2, 7); + UNIT_ASSERT(d.size() == 9); + UNIT_ASSERT(d[0] == 7); + UNIT_ASSERT(d[1] == 7); + + //Insertion of several elements after begin + //There is more elements to insert than elements before insertion position + d.insert(d.begin() + 1, 2, 8); + UNIT_ASSERT(d.size() == 11); + UNIT_ASSERT(d[1] == 8); + UNIT_ASSERT(d[2] == 8); + + //There is less elements to insert than elements before insertion position + d.insert(d.begin() + 3, 2, 9); + UNIT_ASSERT(d.size() == 13); + UNIT_ASSERT(d[3] == 9); + UNIT_ASSERT(d[4] == 9); + + //Insertion of several elements at end: + d.insert(d.end(), 2, 10); + UNIT_ASSERT(d.size() == 15); + UNIT_ASSERT(d[14] == 10); + UNIT_ASSERT(d[13] == 10); + + //Insertion of several elements before last: + //There is more elements to insert than elements after insertion position + d.insert(d.end() - 1, 2, 11); + UNIT_ASSERT(d.size() == 17); + UNIT_ASSERT(d[15] == 11); + UNIT_ASSERT(d[14] == 11); + + //There is less elements to insert than elements after insertion position + d.insert(d.end() - 3, 2, 12); + UNIT_ASSERT(d.size() == 19); + UNIT_ASSERT(d[15] == 12); + UNIT_ASSERT(d[14] == 12); +} + +void TDequeTest::TestAt() { + TDeque d; + TDeque const& cd = d; + + d.push_back(10); + UNIT_ASSERT(d.at(0) == 10); + d.at(0) = 20; + UNIT_ASSERT(cd.at(0) == 20); + UNIT_ASSERT_EXCEPTION(d.at(1) = 20, std::out_of_range); +} + +void TDequeTest::TestAutoRef() { + int i; + TDeque ref; + for (i = 0; i < 5; ++i) { + ref.push_back(i); + } + + TDeque> d_d_int(1, ref); + d_d_int.push_back(d_d_int[0]); + d_d_int.push_back(ref); + d_d_int.push_back(d_d_int[0]); + d_d_int.push_back(d_d_int[0]); + d_d_int.push_back(ref); + + for (i = 0; i < 5; ++i) { + UNIT_ASSERT(d_d_int[i] == ref); + } +} + +void TDequeTest::TestErase() { + TDeque dint; + dint.push_back(3); + dint.push_front(2); + dint.push_back(4); + dint.push_front(1); + dint.push_back(5); + dint.push_front(0); + dint.push_back(6); + + TDeque::iterator it(dint.begin() + 1); + UNIT_ASSERT(*it == 1); + + dint.erase(dint.begin()); + UNIT_ASSERT(*it == 1); + + it = dint.end() - 2; + UNIT_ASSERT(*it == 5); + + dint.erase(dint.end() - 1); + UNIT_ASSERT(*it == 5); + + dint.push_back(6); + dint.push_front(0); + + it = dint.begin() + 2; + UNIT_ASSERT(*it == 2); + + dint.erase(dint.begin(), dint.begin() + 2); + UNIT_ASSERT(*it == 2); + + it = dint.end() - 3; + UNIT_ASSERT(*it == 4); + + dint.erase(dint.end() - 2, dint.end()); + UNIT_ASSERT(*it == 4); +} diff --git a/util/generic/deque_ut.pyx b/util/generic/deque_ut.pyx new file mode 100644 index 00000000000..42cec426475 --- /dev/null +++ b/util/generic/deque_ut.pyx @@ -0,0 +1,66 @@ +from libcpp.deque cimport deque +from util.generic.deque cimport TDeque + +import pytest +import unittest + + +class TestDeque(unittest.TestCase): + def test_ctor1(self): + cdef TDeque[int] tmp = TDeque[int]() + self.assertEqual(tmp.size(), 0) + + def test_ctor2(self): + cdef TDeque[int] tmp = TDeque[int](10) + self.assertEqual(tmp.size(), 10) + self.assertEqual(tmp[0], 0) + + def test_ctor3(self): + cdef TDeque[int] tmp = TDeque[int](10, 42) + self.assertEqual(tmp.size(), 10) + self.assertEqual(tmp[0], 42) + + def test_ctor4(self): + cdef TDeque[int] tmp = TDeque[int](10, 42) + cdef TDeque[int] tmp2 = TDeque[int](tmp) + self.assertEqual(tmp2.size(), 10) + self.assertEqual(tmp2[0], 42) + + def test_operator_assign(self): + cdef TDeque[int] tmp2 + tmp2.push_back(1) + tmp2.push_back(2) + + cdef TDeque[int] tmp3 + tmp3.push_back(1) + tmp3.push_back(3) + + self.assertEqual(tmp2[1], 2) + self.assertEqual(tmp3[1], 3) + + tmp3 = tmp2 + + self.assertEqual(tmp2[1], 2) + self.assertEqual(tmp3[1], 2) + + def test_compare(self): + cdef TDeque[int] tmp1 + tmp1.push_back(1) + tmp1.push_back(2) + + cdef TDeque[int] tmp2 + tmp2.push_back(1) + tmp2.push_back(2) + + cdef TDeque[int] tmp3 + tmp3.push_back(1) + tmp3.push_back(3) + + self.assertTrue(tmp1 == tmp2) + self.assertTrue(tmp1 != tmp3) + + self.assertTrue(tmp1 < tmp3) + self.assertTrue(tmp1 <= tmp3) + + self.assertTrue(tmp3 > tmp1) + self.assertTrue(tmp3 >= tmp1) \ No newline at end of file diff --git a/util/generic/enum_range.cpp b/util/generic/enum_range.cpp new file mode 100644 index 00000000000..c0a97177fe3 --- /dev/null +++ b/util/generic/enum_range.cpp @@ -0,0 +1 @@ +#include "enum_range.h" diff --git a/util/generic/enum_range.h b/util/generic/enum_range.h new file mode 100644 index 00000000000..a9f78a291e8 --- /dev/null +++ b/util/generic/enum_range.h @@ -0,0 +1,72 @@ +#pragma once + +#include + +// This tiny header is to define value ranges at compile time in enums and enum class/struct types. +// +// enum class E1 { +// A, +// B, +// C, +// }; +// Y_DEFINE_ENUM_MINMAX(E1, A, C); +// +// or +// +// enum class E2 { +// A, +// B, +// C, +// }; +// Y_DEFINE_ENUM_MAX(E2, C); +// +// Notes: +// * use Y_DEFINE_ENUM_MINMAX / Y_DEFINE_ENUM_MINMAX if your enum is defined in a namespace +// * use Y_DEFINE_ENUM_MINMAX_F / Y_DEFINE_ENUM_MINMAX_F if your enum is defined in a class/struct +// * use shortened version Y_DEFINE_ENUM_MAX / Y_DEFINE_ENUM_MAX_F if enum begin is 0 +// * add Y_DEFINE_xxx macro immediately after enum definition +// +// Usage examples: +// TEnumRange::Min // min value of range in enum type +// TEnumRange::Max // max value of range in enum type +// TEnumRange::UnderlyingMin // min value of range in underlying type +// TEnumRange::UnderlyingMax // max value of range in underlying type + +void _YRegisterEnumRange(...); + +namespace NDetail::NEnumRange { + + template + struct TEnumRange { + static_assert(std::is_enum_v, ""); + + using TEnum = E; + using TUnderlying = std::underlying_type_t; + + static constexpr TEnum Min = _Min; + static constexpr TEnum Max = _Max; + + static constexpr TUnderlying UnderlyingMin = static_cast(Min); + static constexpr TUnderlying UnderlyingMax = static_cast(Max); + + static_assert(UnderlyingMin <= UnderlyingMax, "Invalid enum range"); + }; +} + +#define Y_DEFINE_ENUM_MINMAX_IMPL(PREFIX, E, Min, Max) \ + PREFIX ::NDetail::NEnumRange::TEnumRange _YRegisterEnumRange(const E*) + +#define Y_DEFINE_ENUM_MINMAX(E, Min, Max) \ + Y_DEFINE_ENUM_MINMAX_IMPL([[maybe_unused]], E, E::Min, E::Max) + +#define Y_DEFINE_ENUM_MAX(E, Max) \ + Y_DEFINE_ENUM_MINMAX_IMPL([[maybe_unused]], E, E{}, E::Max) + +#define Y_DEFINE_ENUM_MINMAX_FRIEND(E, Min, Max) \ + Y_DEFINE_ENUM_MINMAX_IMPL(friend, E, E::Min, E::Max) + +#define Y_DEFINE_ENUM_MAX_FRIEND(E, Max) \ + Y_DEFINE_ENUM_MINMAX_IMPL(friend, E, E{}, E::Max) + +template +using TEnumRange = decltype(_YRegisterEnumRange(static_cast(nullptr))); diff --git a/util/generic/enum_range_ut.cpp b/util/generic/enum_range_ut.cpp new file mode 100644 index 00000000000..38caeae61b6 --- /dev/null +++ b/util/generic/enum_range_ut.cpp @@ -0,0 +1,237 @@ +#include "enum_range.h" + +#include +#include +#include + +class TEnumRangeTest: public TTestBase { + UNIT_TEST_SUITE(TEnumRangeTest); + UNIT_TEST(TestGlobalEnumRange) + UNIT_TEST(TestNamedNamespaceEnumRange) + UNIT_TEST(TestAnonNamespaceEnumRange) + UNIT_TEST(TestMemberEnumRange) + UNIT_TEST_SUITE_END(); + +protected: + void TestGlobalEnumRange(); + void TestNamedNamespaceEnumRange(); + void TestAnonNamespaceEnumRange(); + void TestMemberEnumRange(); +}; + +UNIT_TEST_SUITE_REGISTRATION(TEnumRangeTest); + +#define Y_DEFINE_ENUM_SERIALIZATION(Enum, Prefix) \ + Y_DECLARE_OUT_SPEC(inline, Enum, stream, value) { \ + switch (value) { \ + using enum Enum; \ + case Y_CAT(Prefix, 1): \ + stream << Y_STRINGIZE(Y_CAT(Prefix, 1)); \ + case Y_CAT(Prefix, 2): \ + stream << Y_STRINGIZE(Y_CAT(Prefix, 2)); \ + case Y_CAT(Prefix, 3): \ + stream << Y_STRINGIZE(Y_CAT(Prefix, 3)); \ + } \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +enum class EGlobal1 { + G11, + G12, + G13 +}; +Y_DEFINE_ENUM_MINMAX(EGlobal1, G11, G13); + +enum EGlobal2 { + G21 = 5, + G22, + G23 = 9 +}; +Y_DEFINE_ENUM_MINMAX(EGlobal2, G21, G23); + +enum class EGlobal3 { + G31, + G32, + G33 +}; +Y_DEFINE_ENUM_MAX(EGlobal3, G33); + +enum EGlobal4 { + G41, + G42, + G43 +}; +Y_DEFINE_ENUM_MAX(EGlobal4, G43); + +Y_DEFINE_ENUM_SERIALIZATION(EGlobal1, G1); +Y_DEFINE_ENUM_SERIALIZATION(EGlobal2, G2); +Y_DEFINE_ENUM_SERIALIZATION(EGlobal3, G3); +Y_DEFINE_ENUM_SERIALIZATION(EGlobal4, G4); + +void TEnumRangeTest::TestGlobalEnumRange() { + UNIT_ASSERT_VALUES_EQUAL(EGlobal1::G11, TEnumRange::Min); + UNIT_ASSERT_VALUES_EQUAL(EGlobal1::G13, TEnumRange::Max); + UNIT_ASSERT_VALUES_EQUAL(EGlobal2::G21, TEnumRange::Min); + UNIT_ASSERT_VALUES_EQUAL(EGlobal2::G23, TEnumRange::Max); + UNIT_ASSERT_VALUES_EQUAL(EGlobal3::G31, TEnumRange::Min); + UNIT_ASSERT_VALUES_EQUAL(EGlobal3::G33, TEnumRange::Max); + UNIT_ASSERT_VALUES_EQUAL(EGlobal4::G41, TEnumRange::Min); + UNIT_ASSERT_VALUES_EQUAL(EGlobal4::G43, TEnumRange::Max); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(5, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(9, TEnumRange::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange::UnderlyingMax); +} + +namespace NNamespace { + enum class ENamed1 { + N11, + N12, + N13 + }; + Y_DEFINE_ENUM_MINMAX(ENamed1, N11, N13); + + enum ENamed2 { + N21 = 5, + N22, + N23 = 9 + }; + Y_DEFINE_ENUM_MINMAX(ENamed2, N21, N23); + + enum class ENamed3 { + N31, + N32, + N33 + }; + Y_DEFINE_ENUM_MAX(ENamed3, N33); + + enum ENamed4 { + N41, + N42, + N43 + }; + Y_DEFINE_ENUM_MAX(ENamed4, N43); +} + +Y_DEFINE_ENUM_SERIALIZATION(NNamespace::ENamed1, N1); +Y_DEFINE_ENUM_SERIALIZATION(NNamespace::ENamed2, N2); +Y_DEFINE_ENUM_SERIALIZATION(NNamespace::ENamed3, N3); +Y_DEFINE_ENUM_SERIALIZATION(NNamespace::ENamed4, N4); + +void TEnumRangeTest::TestNamedNamespaceEnumRange() { + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed1::N11, TEnumRange::Min); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed1::N13, TEnumRange::Max); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed2::N21, TEnumRange::Min); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed2::N23, TEnumRange::Max); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed3::N31, TEnumRange::Min); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed3::N33, TEnumRange::Max); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed4::N41, TEnumRange::Min); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed4::N43, TEnumRange::Max); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(5, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(9, TEnumRange::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange::UnderlyingMax); +} + +namespace { + enum class EAnon1 { + A11, + A12, + A13 + }; + Y_DEFINE_ENUM_MINMAX(EAnon1, A11, A13); + + enum EAnon2 { + A21 = 5, + A22, + A23 = 9 + }; + Y_DEFINE_ENUM_MINMAX(EAnon2, A21, A23); + + enum class EAnon3 { + A31, + A32, + A33 + }; + Y_DEFINE_ENUM_MAX(EAnon3, A33); + + enum EAnon4 { + A41, + A42, + A43 + }; + Y_DEFINE_ENUM_MAX(EAnon4, A43); +} + +void TEnumRangeTest::TestAnonNamespaceEnumRange() { + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(5, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(9, TEnumRange::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange::UnderlyingMax); +} + +struct TTestStruct { + enum class EMember1 { + M11, + M12, + M13 + }; + Y_DEFINE_ENUM_MINMAX_FRIEND(EMember1, M11, M13); + + enum EMember2 { + M21 = 5, + M22, + M23 = 9 + }; + Y_DEFINE_ENUM_MINMAX_FRIEND(EMember2, M21, M23); + + enum class EMember3 { + M31, + M32, + M33 + }; + Y_DEFINE_ENUM_MAX_FRIEND(EMember3, M33); + + enum EMember4 { + M41, + M42, + M43 + }; + Y_DEFINE_ENUM_MAX_FRIEND(EMember4, M43); +}; + +Y_DEFINE_ENUM_SERIALIZATION(TTestStruct::EMember1, M1); +Y_DEFINE_ENUM_SERIALIZATION(TTestStruct::EMember2, M2); +Y_DEFINE_ENUM_SERIALIZATION(TTestStruct::EMember3, M3); +Y_DEFINE_ENUM_SERIALIZATION(TTestStruct::EMember4, M4); + +void TEnumRangeTest::TestMemberEnumRange() { + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember1::M11, TEnumRange::Min); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember1::M13, TEnumRange::Max); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember2::M21, TEnumRange::Min); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember2::M23, TEnumRange::Max); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember3::M31, TEnumRange::Min); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember3::M33, TEnumRange::Max); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember4::M41, TEnumRange::Min); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember4::M43, TEnumRange::Max); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(5, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(9, TEnumRange::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange::UnderlyingMax); +} diff --git a/util/generic/explicit_type.cpp b/util/generic/explicit_type.cpp new file mode 100644 index 00000000000..65bc80093a7 --- /dev/null +++ b/util/generic/explicit_type.cpp @@ -0,0 +1 @@ +#include "explicit_type.h" diff --git a/util/generic/explicit_type.h b/util/generic/explicit_type.h new file mode 100644 index 00000000000..34e269f1551 --- /dev/null +++ b/util/generic/explicit_type.h @@ -0,0 +1,42 @@ +#pragma once + +#include "typetraits.h" + +/** + * Helper type that can be used as one of the parameters in function declaration + * to limit the number of types this function can be called with. + * + * Example usage: + * @code + * void CharOnlyFunction(TExplicitType value); + * void AnythingFunction(char value); + * + * CharOnlyFunction('c'); // Works. + * CharOnlyFunction(1); // Compilation error. + * CharOnlyFunction(1ull); // Compilation error. + * + * AnythingFunction('c'); // Works. + * AnythingFunction(1); // Works. + * AnythingFunction(1ull); // Works. + * @endcode + */ +template +class TExplicitType { +public: + template + TExplicitType(const OtherT& value, std::enable_if_t::value>* = nullptr) noexcept + : Value_(value) + { + } + + const T& Value() const noexcept { + return Value_; + } + + operator const T&() const noexcept { + return Value_; + } + +private: + const T& Value_; +}; diff --git a/util/generic/explicit_type_ut.cpp b/util/generic/explicit_type_ut.cpp new file mode 100644 index 00000000000..0d443b35895 --- /dev/null +++ b/util/generic/explicit_type_ut.cpp @@ -0,0 +1,54 @@ +#include "explicit_type.h" + +#include + +struct TCallableBase { +public: + using TYes = char; + using TNo = struct { + TYes dummy[32]; + }; + + template + static TNo Test(const T&, const Arg&, ...); + + template + static TYes Test(const T&, const Arg&, int, decltype(std::declval()(std::declval()))* = nullptr); +}; + +template +struct TCallable: public TCallableBase { + enum { + Result = sizeof(Test(std::declval(), std::declval(), 1)) == sizeof(TYes) + }; +}; + +template +struct TExplicitlyCallable { + void operator()(TExplicitType) { + } +}; + +struct IntConvertible { + operator int() { + return 1; + } +}; + +struct IntConstructible { + IntConstructible(const int&) { + } +}; + +Y_UNIT_TEST_SUITE(TestExplicitType) { + Y_UNIT_TEST(Test1) { + UNIT_ASSERT_VALUES_EQUAL(static_cast(TCallable, char>::Result), true); + UNIT_ASSERT_VALUES_EQUAL(static_cast(TCallable, int>::Result), false); + UNIT_ASSERT_VALUES_EQUAL(static_cast(TCallable, wchar_t>::Result), false); + UNIT_ASSERT_VALUES_EQUAL(static_cast(TCallable, int>::Result), true); + UNIT_ASSERT_VALUES_EQUAL(static_cast(TCallable, IntConvertible>::Result), false); + UNIT_ASSERT_VALUES_EQUAL(static_cast(TCallable, IntConstructible>::Result), true); + UNIT_ASSERT_VALUES_EQUAL(static_cast(TCallable, IntConvertible>::Result), false); + UNIT_ASSERT_VALUES_EQUAL(static_cast(TCallable, int>::Result), false); + } +} diff --git a/util/generic/fastqueue.cpp b/util/generic/fastqueue.cpp new file mode 100644 index 00000000000..140b3ef63e0 --- /dev/null +++ b/util/generic/fastqueue.cpp @@ -0,0 +1 @@ +#include "fastqueue.h" diff --git a/util/generic/fastqueue.h b/util/generic/fastqueue.h new file mode 100644 index 00000000000..1fee5b86f6e --- /dev/null +++ b/util/generic/fastqueue.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include "ptr.h" + +template +class TFastQueue { + struct THelper: public TObjectFromPool, public TIntrusiveListItem { + inline THelper(const T& t) + : Obj(t) + { + } + + T Obj; + }; + +public: + inline TFastQueue() + : Pool_(TDefaultAllocator::Instance()) + , Size_(0) + { + } + + inline void Push(const T& t) { + Queue_.PushFront(new (&Pool_) THelper(t)); + ++Size_; + } + + inline T Pop() { + Y_ASSERT(!this->Empty()); + + THolder tmp(Queue_.PopBack()); + --Size_; + + return tmp->Obj; + } + + inline size_t Size() const noexcept { + return Size_; + } + + Y_PURE_FUNCTION inline bool Empty() const noexcept { + return !this->Size(); + } + + inline explicit operator bool() const noexcept { + return !this->Empty(); + } + +private: + typename THelper::TPool Pool_; + TIntrusiveListWithAutoDelete Queue_; + size_t Size_; +}; diff --git a/util/generic/flags.cpp b/util/generic/flags.cpp new file mode 100644 index 00000000000..ced510d4fb7 --- /dev/null +++ b/util/generic/flags.cpp @@ -0,0 +1,29 @@ +#include "flags.h" + +#include +#include + +void ::NPrivate::PrintFlags(IOutputStream& stream, ui64 value, size_t size) { + /* Note that this function is in cpp because we need to break circular + * dependency between TFlags and ENumberFormat. */ + stream << "TFlags("; + + switch (size) { + case 1: + stream << Bin(static_cast(value), HF_FULL); + break; + case 2: + stream << Bin(static_cast(value), HF_FULL); + break; + case 4: + stream << Bin(static_cast(value), HF_FULL); + break; + case 8: + stream << Bin(static_cast(value), HF_FULL); + break; + default: + Y_ABORT_UNLESS(false); + } + + stream << ")"; +} diff --git a/util/generic/flags.h b/util/generic/flags.h new file mode 100644 index 00000000000..e11163a2f45 --- /dev/null +++ b/util/generic/flags.h @@ -0,0 +1,258 @@ +#pragma once + +#include + +#include +#include +#include + +class IOutputStream; +namespace NPrivate { + void PrintFlags(IOutputStream& stream, ui64 value, size_t size); +} + +/** + * `TFlags` wrapper provides a type-safe mechanism for storing OR combinations + * of enumeration values. + * + * This class is intended to be used mainly via helper macros. For example: + * @code + * class TAligner { + * public: + * enum EOrientation { + * Vertical = 1, + * Horizontal = 2 + * }; + * Y_DECLARE_FLAGS(EOrientations, EOrientation); + * + * // ... + * }; + * + * Y_DECLARE_OPERATORS_FOR_FLAGS(TAligner::EOrientations); + * @endcode + */ +template +class TFlags { + static_assert(std::is_enum::value, "Expecting an enumeration here."); + +public: + using TEnum = Enum; + using TInt = std::underlying_type_t; + + constexpr TFlags(std::nullptr_t = 0) + : Value_(0) + { + } + + constexpr TFlags(Enum value) + : Value_(static_cast(value)) + { + } + + /* Generated copy/move ctor/assignment are OK. */ + + constexpr operator TInt() const { + return Value_; + } + + constexpr TInt ToBaseType() const { + return Value_; + } + + constexpr static TFlags FromBaseType(TInt value) { + return TFlags(TFlag(value)); + } + + constexpr friend TFlags operator|(TFlags l, TFlags r) { + return TFlags(TFlag(l.Value_ | r.Value_)); + } + + constexpr friend TFlags operator|(TEnum l, TFlags r) { + return TFlags(TFlag(static_cast(l) | r.Value_)); + } + + constexpr friend TFlags operator|(TFlags l, TEnum r) { + return TFlags(TFlag(l.Value_ | static_cast(r))); + } + + constexpr friend TFlags operator^(TFlags l, TFlags r) { + return TFlags(TFlag(l.Value_ ^ r.Value_)); + } + + constexpr friend TFlags + operator^(TEnum l, TFlags r) { + return TFlags(TFlag(static_cast(l) ^ r.Value_)); + } + + constexpr friend TFlags + operator^(TFlags l, TEnum r) { + return TFlags(TFlag(l.Value_ ^ static_cast(r))); + } + + constexpr friend TFlags + operator&(TFlags l, TFlags r) { + return TFlags(TFlag(l.Value_ & r.Value_)); + } + + constexpr friend TFlags operator&(TEnum l, TFlags r) { + return TFlags(TFlag(static_cast(l) & r.Value_)); + } + + constexpr friend TFlags operator&(TFlags l, TEnum r) { + return TFlags(TFlag(l.Value_ & static_cast(r))); + } + + constexpr friend bool operator==(TFlags l, TFlags r) { + return l.Value_ == r.Value_; + } + + constexpr friend bool operator==(TEnum l, TFlags r) { + return static_cast(l) == r.Value_; + } + + constexpr friend bool operator==(TFlags l, TEnum r) { + return l.Value_ == static_cast(r); + } + + constexpr friend bool operator!=(TFlags l, TFlags r) { + return l.Value_ != r.Value_; + } + + constexpr friend bool operator!=(TEnum l, TFlags r) { + return static_cast(l) != r.Value_; + } + + constexpr friend bool operator!=(TFlags l, TEnum r) { + return l.Value_ != static_cast(r); + } + + TFlags& operator&=(TFlags flags) { + *this = *this & flags; + return *this; + } + + TFlags& operator&=(Enum value) { + *this = *this & value; + return *this; + } + + TFlags& operator|=(TFlags flags) { + *this = *this | flags; + return *this; + } + + TFlags& operator|=(Enum value) { + *this = *this | value; + return *this; + } + + TFlags& operator^=(TFlags flags) { + *this = *this ^ flags; + return *this; + } + + TFlags& operator^=(Enum flags) { + *this = *this ^ flags; + return *this; + } + + constexpr TFlags operator~() const { + return TFlags(TFlag(~Value_)); + } + + constexpr bool operator!() const { + return !Value_; + } + + constexpr explicit operator bool() const { + return Value_; + } + + constexpr bool HasFlag(Enum value) const { + return (Value_ & static_cast(value)) == static_cast(value); + } + + constexpr bool HasFlags(TFlags flags) const { + return (Value_ & flags.Value_) == flags.Value_; + } + + constexpr bool HasAnyOfFlags(TFlags flags) const { + return (Value_ & flags.Value_) != 0; + } + + TFlags RemoveFlag(Enum value) { + Value_ &= ~static_cast(value); + return *this; + } + + TFlags RemoveFlags(TFlags flags) { + Value_ &= ~flags.Value_; + return *this; + } + + friend IOutputStream& operator<<(IOutputStream& stream, const TFlags& flags) { + ::NPrivate::PrintFlags(stream, static_cast(flags.Value_), sizeof(TInt)); + return stream; + } + +private: + struct TFlag { + constexpr TFlag() { + } + constexpr explicit TFlag(TInt value) + : Value(value) + { + } + + TInt Value = 0; + }; + + constexpr explicit TFlags(TFlag value) + : Value_(value.Value) + { + } + +private: + TInt Value_; +}; + +template +struct TPodTraits<::TFlags> { + enum { + IsPod = TTypeTraits::IsPod + }; +}; + +template +struct THash<::TFlags> { + size_t operator()(const TFlags& flags) const noexcept { + return THash::TInt>()(flags); + } +}; + +/** + * This macro defines a flags type for the provided enum. + * + * @param FLAGS Name of the flags type to declare. + * @param ENUM Name of the base enum type to use. + */ +#define Y_DECLARE_FLAGS(FLAGS, ENUM) \ + using FLAGS = ::TFlags + +/** + * This macro declares global operator functions for enum base of `FLAGS` type. + * This way operations on individual enum values will provide a type-safe + * `TFlags` object. + * + * @param FLAGS Flags type to declare operator for. + */ +#define Y_DECLARE_OPERATORS_FOR_FLAGS(FLAGS) \ + Y_DECLARE_UNUSED \ + constexpr inline FLAGS operator|(FLAGS::TEnum l, FLAGS::TEnum r) { \ + return FLAGS(l) | r; \ + } \ + Y_DECLARE_UNUSED \ + constexpr inline FLAGS operator~(FLAGS::TEnum value) { \ + return ~FLAGS(value); \ + } \ + Y_SEMICOLON_GUARD diff --git a/util/generic/flags_ut.cpp b/util/generic/flags_ut.cpp new file mode 100644 index 00000000000..fa2d4464c94 --- /dev/null +++ b/util/generic/flags_ut.cpp @@ -0,0 +1,127 @@ +#include + +#include "flags.h" + +enum ETestFlag1: ui16 { + Test1 = 1, + Test2 = 2, + Test4 = 4, + Test8 = 8 +}; +Y_DECLARE_FLAGS(ETest1, ETestFlag1); +Y_DECLARE_OPERATORS_FOR_FLAGS(ETest1); + +static_assert(TTypeTraits::IsPod, "flags should be POD type"); + +enum class ETestFlag2 { + Test1 = 1, + Test2 = 2, + Test4 = 4, + Test8 = 8 +}; +Y_DECLARE_FLAGS(ETest2, ETestFlag2); +Y_DECLARE_OPERATORS_FOR_FLAGS(ETest2); + +namespace { + // won't compile without Y_DECLARE_UNUSED + enum class ETestFlag3 { One = 1, + Two = 2, + Three = 3 }; + Y_DECLARE_FLAGS(ETestFlags3, ETestFlag3); + Y_DECLARE_OPERATORS_FOR_FLAGS(ETestFlags3); +} + +Y_UNIT_TEST_SUITE(TFlagsTest) { + template + void TestEnum() { + { + auto i = Enum::Test1 | Enum::Test2; + + UNIT_ASSERT((std::is_same>::value)); + UNIT_ASSERT((std::is_same>::value)); + UNIT_ASSERT(!(std::is_same::value)); + UNIT_ASSERT_VALUES_EQUAL(sizeof(Enum), sizeof(TFlags)); + + UNIT_ASSERT(i.HasFlag(Enum::Test1)); + UNIT_ASSERT(i.HasFlag(Enum::Test4) == false); + UNIT_ASSERT(i.HasFlags(Enum::Test1)); + UNIT_ASSERT(i.HasFlags(Enum::Test4) == false); + UNIT_ASSERT(i.HasFlags(Enum::Test1 | Enum::Test4) == false); + UNIT_ASSERT(i.HasAnyOfFlags(Enum::Test1)); + UNIT_ASSERT(i.HasAnyOfFlags(Enum::Test4) == false); + UNIT_ASSERT(i.HasAnyOfFlags(Enum::Test1 | Enum::Test4)); + + i |= Enum::Test4; + i ^= Enum::Test2; + UNIT_ASSERT_EQUAL(i, Enum::Test4 | Enum::Test1); + UNIT_ASSERT_EQUAL(i & Enum::Test1, i & ~Enum::Test4); + UNIT_ASSERT(i & Enum::Test4); + UNIT_ASSERT_UNEQUAL(i, ~i); + UNIT_ASSERT_EQUAL(i, ~~i); + } + { + auto i = Enum::Test1 | Enum::Test2; + i.RemoveFlag(Enum::Test1); + UNIT_ASSERT_EQUAL(i, TFlags(Enum::Test2)); + } + { + auto i = Enum::Test1 | Enum::Test2; + i.RemoveFlags(Enum::Test1); + UNIT_ASSERT_EQUAL(i, TFlags(Enum::Test2)); + } + { + auto i = Enum::Test1 | Enum::Test2; + i.RemoveFlags(Enum::Test1 | Enum::Test2); + UNIT_ASSERT_EQUAL(i, TFlags()); + } + } + + Y_UNIT_TEST(TestFlags) { + TestEnum(); + TestEnum(); + } + + Y_UNIT_TEST(TestZero) { + /* This code should simply compile. */ + + ETest1 f = 0; + f = 0; + f = ETest1(0); + + ETest1 ff(0); + ff = 0; + } + + Y_UNIT_TEST(TestOutput) { + ETest1 value0 = nullptr, value1 = Test1, value7 = Test1 | Test2 | Test4; + + UNIT_ASSERT_VALUES_EQUAL(ToString(value0), "TFlags(0000000000000000)"); + UNIT_ASSERT_VALUES_EQUAL(ToString(value1), "TFlags(0000000000000001)"); + UNIT_ASSERT_VALUES_EQUAL(ToString(value7), "TFlags(0000000000000111)"); + } + + Y_UNIT_TEST(TestHash) { + ETest1 value0 = nullptr, value1 = Test1; + + THashMap hash; + hash[value0] = 0; + hash[value1] = 1; + + UNIT_ASSERT_VALUES_EQUAL(hash[value0], 0); + UNIT_ASSERT_VALUES_EQUAL(hash[value1], 1); + } + + Y_UNIT_TEST(TestBaseType) { + ui16 goodValue = 7; + auto goodFlags = ETest1::FromBaseType(goodValue); + UNIT_ASSERT(goodFlags& ETestFlag1::Test1); + UNIT_ASSERT(goodFlags& ETestFlag1::Test2); + UNIT_ASSERT(goodFlags& ETestFlag1::Test4); + UNIT_ASSERT_VALUES_EQUAL(goodValue, goodFlags.ToBaseType()); + + // Passed value is not checked, but preserved as is + ui16 badValue = 1024; + auto badFlags = ETest1::FromBaseType(badValue); + UNIT_ASSERT_VALUES_EQUAL(badValue, badFlags.ToBaseType()); + } +} diff --git a/util/generic/function.cpp b/util/generic/function.cpp new file mode 100644 index 00000000000..d4b693e3fa7 --- /dev/null +++ b/util/generic/function.cpp @@ -0,0 +1 @@ +#include "function.h" diff --git a/util/generic/function.h b/util/generic/function.h new file mode 100644 index 00000000000..859cc3f8493 --- /dev/null +++ b/util/generic/function.h @@ -0,0 +1,118 @@ +#pragma once + +#include "typetraits.h" +#include "typelist.h" + +#include + +namespace NPrivate { + template + struct TRemoveClassImpl { + using TSignature = F; + }; + +#define Y_EMPTY_REF_QUALIFIER +#define Y_FOR_EACH_REF_QUALIFIERS_COMBINATION(XX) \ + XX(Y_EMPTY_REF_QUALIFIER) \ + XX(&) \ + XX(&&) \ + XX(const) \ + XX(const&) \ + XX(const&&) + +#define Y_DECLARE_REMOVE_CLASS_IMPL(qualifiers) \ + template \ + struct TRemoveClassImpl { \ + typedef R TSignature(Args...); \ + }; + + Y_FOR_EACH_REF_QUALIFIERS_COMBINATION(Y_DECLARE_REMOVE_CLASS_IMPL) +#undef Y_DECLARE_REMOVE_CLASS_IMPL + + template + struct TRemoveNoExceptImpl { + using Type = T; + }; + + template + struct TRemoveNoExceptImpl { + using Type = R(Args...); + }; + +#define Y_DECLARE_REMOVE_NOEXCEPT_IMPL(qualifiers) \ + template \ + struct TRemoveNoExceptImpl { \ + using Type = R (C::*)(Args...); \ + }; + + Y_FOR_EACH_REF_QUALIFIERS_COMBINATION(Y_DECLARE_REMOVE_NOEXCEPT_IMPL) +#undef Y_DECLARE_REMOVE_NOEXCEPT_IMPL + +#undef Y_FOR_EACH_REF_QUALIFIERS_COMBINATION +#undef Y_EMPTY_REF_QUALIFIER + + template + using TRemoveNoExcept = typename TRemoveNoExceptImpl::Type; + + template + using TRemoveClass = typename TRemoveClassImpl>::TSignature; + + template + struct TFuncInfo { + using TSignature = TRemoveClass; + }; + + template + struct TFuncInfo { + using TResult = R; + typedef R TSignature(Args...); + }; +} + +template +using TFunctionSignature = typename ::NPrivate::TFuncInfo<::NPrivate::TRemoveClass>>>::TSignature; + +template +struct TCallableTraits: public TCallableTraits> { +}; + +template +struct TCallableTraits { + using TResult = R; + using TArgs = TTypeList; + typedef R TSignature(Args...); +}; + +template +using TFunctionResult = typename TCallableTraits::TResult; + +template +using TFunctionArgs = typename TCallableTraits::TArgs; + +template +struct TFunctionArgImpl { + using TArgs = TFunctionArgs; + using TResult = typename TArgs::template TGet; +}; + +template +using TFunctionArg = typename TFunctionArgImpl::TResult; + +// temporary before std::apply appearance + +template +auto ApplyImpl(F&& f, Tuple&& t, std::index_sequence) { + return f(std::get(std::forward(t))...); +} + +// change to std::apply after c++ 17 +template +auto Apply(F&& f, Tuple&& t) { + return ApplyImpl(f, t, std::make_index_sequence>::value>{}); +} + +// change to std::apply after c++ 17 +template +auto Apply(F&& f, std::tuple<>) { + return f(); +} diff --git a/util/generic/function_ref.cpp b/util/generic/function_ref.cpp new file mode 100644 index 00000000000..9aab36fec7c --- /dev/null +++ b/util/generic/function_ref.cpp @@ -0,0 +1 @@ +#include "function_ref.h" diff --git a/util/generic/function_ref.h b/util/generic/function_ref.h new file mode 100644 index 00000000000..814f0745bfd --- /dev/null +++ b/util/generic/function_ref.h @@ -0,0 +1,139 @@ +#pragma once + +#include +#include + +#include + +namespace NPrivate { + + template + struct TIsNoexcept; + + template + struct TIsNoexcept { + static constexpr bool Value = false; + }; + + template + struct TIsNoexcept { + static constexpr bool Value = true; + }; + +} // namespace NPrivate + +template ::Value> +class TFunctionRef; + +template +class TFunctionRef { +public: + using TSignature = Ret(Args...) noexcept(IsNoexcept); + +private: + union TErasedCallable { + const void* Functor; + void (*Function)(); + }; + using TProxy = Ret (*)(TErasedCallable callable, Args...); + + // Making this a lambda inside TFunctionRef ctor caused: + // "error: cannot compile this forwarded non-trivially copyable parameter yet" + // on clang-win-i686-release. + // + // Using correct noexcept specifiers here (noexcept(IsNoexcept)) caused miscompilation on clang: + // https://github.com/llvm/llvm-project/issues/55280. + template + static Ret InvokeErasedFunctor(TErasedCallable callable, Args... args) { + auto& ref = *static_cast*>(callable.Functor); + return static_cast(std::invoke(ref, std::forward(args)...)); + } + + template + static Ret InvokeErasedFunction(TErasedCallable callable, Args... args) { + auto* function = reinterpret_cast(callable.Function); + return static_cast(std::invoke(function, std::forward(args)...)); + } + + template + static constexpr bool IsInvocableUsing = std::conditional_t< + IsNoexcept, + std::is_nothrow_invocable_r, + std::is_invocable_r>::value; + + // clang-format off + template + static constexpr bool IsSuitableFunctor = + IsInvocableUsing + && !std::is_function_v + && !std::is_same_v, TFunctionRef>; + + template + static constexpr bool IsSuitableFunction = + IsInvocableUsing + && std::is_function_v; + // clang-format on + +public: + // Function ref should not be default constructible. + // While the function ref can have empty state (for example, Proxy_ == nullptr), + // It does not make sense in common usage cases. + TFunctionRef() = delete; + + // Construct function ref from a functor. + template >> + TFunctionRef(Functor&& functor) noexcept + : Callable_{ + .Functor = std::addressof(functor), + } + , Proxy_{InvokeErasedFunctor} + { + } + + // Construct function ref from a function pointer. + template >> + TFunctionRef(Function* function) noexcept + : Callable_{ + .Function = reinterpret_cast(function), + } + , Proxy_{InvokeErasedFunction} + { + } + + // Copy ctors & assignment. + // Just copy pointers. + TFunctionRef(const TFunctionRef& rhs) noexcept = default; + TFunctionRef& operator=(const TFunctionRef& rhs) noexcept = default; + + Ret operator()(Args... args) const noexcept(IsNoexcept) { + return Proxy_(Callable_, std::forward(args)...); + } + +private: + TErasedCallable Callable_; + TProxy Proxy_ = nullptr; +}; + +namespace NPrivate { + + template ::TSignature> + struct TIsNothrowInvocable; + + template + struct TIsNothrowInvocable { + static constexpr bool IsNoexcept = std::is_nothrow_invocable_r_v; + using TSignature = Ret(Args...) noexcept(IsNoexcept); + }; + + template + struct TCallableTraitsWithNoexcept { + using TSignature = typename TIsNothrowInvocable::TSignature; + }; + +} // namespace NPrivate + +template +TFunctionRef(Callable&&) -> TFunctionRef::TSignature>; + +template +TFunctionRef(Function*) -> TFunctionRef; diff --git a/util/generic/function_ref_ut.cpp b/util/generic/function_ref_ut.cpp new file mode 100644 index 00000000000..59f9ae35cd8 --- /dev/null +++ b/util/generic/function_ref_ut.cpp @@ -0,0 +1,150 @@ +#include "function_ref.h" + +#include + +Y_UNIT_TEST_SUITE(TestFunctionRef) { + template + struct TTestFunction; + + template + struct TTestFunction { + Ret operator()(Args...) const noexcept(IsNoexcept) { + return {}; + } + }; + + Y_UNIT_TEST(NonDefaultConstructible) { + static_assert(!std::is_default_constructible_v>); + static_assert(!std::is_default_constructible_v>); + static_assert(!std::is_default_constructible_v)>>); + } + + int F1(bool x) { + if (x) + throw 19; + return 42; + } + + int F2(bool x) noexcept { + return 42 + x; + } + + static const TTestFunction C1; + static const TTestFunction C2; + + Y_UNIT_TEST(Noexcept) { + static_assert(std::is_constructible_v, decltype(F1)>); + static_assert(std::is_constructible_v, decltype(F2)>); + static_assert(!std::is_constructible_v, decltype(F1)>); + static_assert(std::is_constructible_v, decltype(F2)>); + + static_assert(std::is_constructible_v, decltype(C1)>); + static_assert(std::is_constructible_v, decltype(C2)>); + static_assert(!std::is_constructible_v, decltype(C1)>); + static_assert(std::is_constructible_v, decltype(C2)>); + } + + Y_UNIT_TEST(Deduction) { + TFunctionRef ref1(F1); + TFunctionRef ref2(F2); + TFunctionRef ref3(C1); + TFunctionRef ref4(C2); + + static_assert(!std::is_nothrow_invocable_r_v); + static_assert(std::is_nothrow_invocable_r_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + + void WithCallback(TFunctionRef); + + void Iterate(int from, int to, TFunctionRef callback) { + while (from < to) { + callback(from++); + } + } + + void IterateNoexcept(int from, int to, TFunctionRef callback) { + while (from < to) { + callback(from++); + } + } + + Y_UNIT_TEST(AsArgument) { + int sum = 0; + Iterate(0, 10, [&](int x) { sum += x; }); + UNIT_ASSERT_EQUAL(sum, 45); + + Iterate(0, 10, [&](int x) noexcept { sum += x; }); + UNIT_ASSERT_EQUAL(sum, 90); + + IterateNoexcept(0, 10, [&](int x) noexcept { sum += x; }); + UNIT_ASSERT_EQUAL(sum, 135); + + auto summer = [&](int x) { sum += x; }; + Iterate(0, 10, summer); + Iterate(0, 10, summer); + Iterate(0, 10, summer); + UNIT_ASSERT_EQUAL(sum, 270); + + TFunctionRef ref = summer; + Iterate(0, 10, ref); + UNIT_ASSERT_EQUAL(sum, 315); + } + + int GlobalSum = 0; + void AddToGlobalSum(int x) { + GlobalSum += x; + } + + Y_UNIT_TEST(FunctionPointer) { + GlobalSum = 0; + Iterate(0, 10, AddToGlobalSum); + UNIT_ASSERT_EQUAL(GlobalSum, 45); + + TFunctionRef ref1 = AddToGlobalSum; + Iterate(0, 10, ref1); + UNIT_ASSERT_EQUAL(GlobalSum, 90); + + TFunctionRef ref2{AddToGlobalSum}; + Iterate(0, 10, ref2); + UNIT_ASSERT_EQUAL(GlobalSum, 135); + } + + Y_UNIT_TEST(Reassign) { + TFunctionRef kek = [](double) { return 42; }; + kek = [](double) { return 19; }; + kek = [](int) { return 22.8; }; + } + + const char* Greet() { + return "Hello, world!"; + } + + Y_UNIT_TEST(ImplicitCasts) { + TFunctionRef ref = [](int x) { return x; }; + ref = [](double x) { return x; }; + ref = [](char x) { return x; }; + + TFunctionRef ref1 = [] { return 0.5; }; + ref1 = [] { return 'a'; }; + ref1 = [] { return 124u; }; + + TFunctionRef ref2{Greet}; + } + + Y_UNIT_TEST(StatelessLambdaLifetime) { + TFunctionRef ref{[](int a, int b) { return a + b; }}; + UNIT_ASSERT_EQUAL(ref(5, 5), 10); + } + + Y_UNIT_TEST(ForwardArguments) { + char x = 'x'; + TFunctionRef, char&)> ref = [](std::unique_ptr ptr, char& ch) { + UNIT_ASSERT_EQUAL(*ptr, 5); + ch = 'a'; + }; + ref(std::make_unique(5), x); + UNIT_ASSERT_EQUAL(x, 'a'); + } +} diff --git a/util/generic/function_ut.cpp b/util/generic/function_ut.cpp new file mode 100644 index 00000000000..f46188aa2a2 --- /dev/null +++ b/util/generic/function_ut.cpp @@ -0,0 +1,138 @@ +#include "function.h" +#include "typetraits.h" + +#include + +Y_UNIT_TEST_SUITE(TestFunctionSignature) { + int FF(double x) { + return (int)x; + } + + int FFN(double x) noexcept { + return (int)x; + } + + int FFF(double x, char xx) { + return (int)x + (int)xx; + } + + int FFFN(double x, char xx) noexcept { + return (int)x + (int)xx; + } + + struct A { + int F(double x) { + return FF(x); + } + + int FN(double x) noexcept { + return FFN(x); + } + + int FC(double x) const { + return FF(x); + } + + int FCN(double x) const noexcept { + return FFN(x); + } + +#define Y_FOR_EACH_REF_QUALIFIED_MEMBERS(XX) \ + XX(AsMutLvalue, &, false) \ + XX(AsMutLvalueN, &, true) \ + XX(AsMutRvalue, &&, false) \ + XX(AsMutRvalueN, &&, true) \ + XX(AsConstLvalue, const&, false) \ + XX(AsConstLvalueN, const&, true) \ + XX(AsConstRvalue, const&&, false) \ + XX(AsConstRvalueN, const&&, true) + +#define Y_ADD_MEMBER(name, qualifiers, isNoexcept) \ + int name(double x) qualifiers noexcept(isNoexcept) { \ + return FF(x); \ + } + + Y_FOR_EACH_REF_QUALIFIED_MEMBERS(Y_ADD_MEMBER) +#undef Y_ADD_MEMBER + }; + + Y_UNIT_TEST(TestPlainFunc) { + UNIT_ASSERT_TYPES_EQUAL(TFunctionSignature, decltype(FF)); + UNIT_ASSERT_TYPES_EQUAL(TFunctionSignature, decltype(FF)); + } + + Y_UNIT_TEST(TestMethod) { + UNIT_ASSERT_TYPES_EQUAL(TFunctionSignature, decltype(FF)); + UNIT_ASSERT_TYPES_EQUAL(TFunctionSignature, decltype(FF)); + UNIT_ASSERT_TYPES_EQUAL(TFunctionSignature, decltype(FF)); + UNIT_ASSERT_TYPES_EQUAL(TFunctionSignature, decltype(FF)); + +#define Y_CHECK_MEMBER(name, qualifiers, isNoexcept) \ + UNIT_ASSERT_TYPES_EQUAL(TFunctionSignature, decltype(FF)); + + Y_FOR_EACH_REF_QUALIFIED_MEMBERS(Y_CHECK_MEMBER) +#undef Y_CHECK_MEMBER + } + + Y_UNIT_TEST(TestLambda) { + auto f = [](double x) -> int { + return FF(x); + }; + + auto fn = [](double x) mutable noexcept -> int { + return FFN(x); + }; + + auto fcn = [](double x) noexcept -> int { + return FFN(x); + }; + + UNIT_ASSERT_TYPES_EQUAL(TFunctionSignature, decltype(FF)); + UNIT_ASSERT_TYPES_EQUAL(TFunctionSignature, decltype(FF)); + UNIT_ASSERT_TYPES_EQUAL(TFunctionSignature, decltype(FF)); + } + + Y_UNIT_TEST(TestFunction) { + std::function f(FF); + + UNIT_ASSERT_TYPES_EQUAL(TFunctionSignature, decltype(FF)); + } + + template + void TestCT() { +#define FA(x) TFunctionArg + + UNIT_ASSERT_TYPES_EQUAL(FA(0), double); + UNIT_ASSERT_TYPES_EQUAL(FA(1), char); + UNIT_ASSERT_TYPES_EQUAL(TFunctionResult, int); + +#undef FA + } + + Y_UNIT_TEST(TestTypeErasureTraits) { + TestCT>(); + } + + Y_UNIT_TEST(TestPlainFunctionTraits) { + TestCT(); + TestCT(); + } + + Y_UNIT_TEST(TestLambdaTraits) { + auto fff = [](double xx, char xxx) -> int { + return FFF(xx, xxx); + }; + + auto fffn = [](double xx, char xxx) mutable noexcept -> int { + return FFFN(xx, xxx); + }; + + auto fffcn = [](double xx, char xxx) noexcept -> int { + return FFFN(xx, xxx); + }; + + TestCT(); + TestCT(); + TestCT(); + } +} diff --git a/util/generic/fwd.cpp b/util/generic/fwd.cpp new file mode 100644 index 00000000000..4214b6df83e --- /dev/null +++ b/util/generic/fwd.cpp @@ -0,0 +1 @@ +#include "fwd.h" diff --git a/util/generic/fwd.h b/util/generic/fwd.h new file mode 100644 index 00000000000..a70e9bb6c3b --- /dev/null +++ b/util/generic/fwd.h @@ -0,0 +1,168 @@ +#pragma once + +#include + +#include + +template > +class TBasicString; + +using TString = TBasicString; +using TUtf16String = TBasicString; +using TUtf32String = TBasicString; + +template > +class TBasicStringBuf; + +using TStringBuf = TBasicStringBuf; +using TWtringBuf = TBasicStringBuf; +using TUtf32StringBuf = TBasicStringBuf; + +//misc +class TBuffer; + +//functors +template +struct TLess; + +template +struct TGreater; + +template +struct TEqualTo; + +template +struct THash; + +//intrusive containers +struct TIntrusiveListDefaultTag; +template +class TIntrusiveList; + +template +class TIntrusiveListWithAutoDelete; + +template +class TIntrusiveSList; + +template +class TAvlTree; + +template +class TRbTree; + +//containers +template > +class TVector; + +template > +class TDeque; + +template > +class TQueue; + +template , class C = TLess> +class TPriorityQueue; + +template , class EqualKey = TEqualTo, class Alloc = std::allocator> +class THashMap; + +template , class EqualKey = TEqualTo, class Alloc = std::allocator> +class THashMultiMap; + +template , class EqualKey = TEqualTo, class Alloc = std::allocator> +class THashSet; + +template , class EqualKey = TEqualTo, class Alloc = std::allocator> +class THashMultiSet; + +template > +class TList; + +template , class A = std::allocator> +class TMap; + +template , class A = std::allocator> +class TMultiMap; + +template , class A = std::allocator> +class TSet; + +template , class A = std::allocator> +class TMultiSet; + +template > +class TStack; + +template +class TBitMap; + +//autopointers +class TDelete; +class TDeleteArray; +class TFree; +class TCopyNew; + +template +class TAutoPtr; + +template +class THolder; + +template +class TRefCounted; + +template +class TDefaultIntrusivePtrOps; + +template +class TSimpleIntrusiveOps; + +template > +class TIntrusivePtr; + +template > +class TIntrusiveConstPtr; + +template > +using TSimpleIntrusivePtr = TIntrusivePtr>; + +template +class TSharedPtr; + +template +class TCopyPtr; + +template +class TCowPtr; + +template +class TPtrArg; + +template +using TArrayHolder = THolder; + +template +using TMallocHolder = THolder; + +template +using TArrayPtr = TAutoPtr; + +template +using TMallocPtr = TAutoPtr; + +//maybe +namespace NMaybe { + struct TPolicyUndefinedExcept; +} + +template +class TMaybe; + +struct TGUID; + +template +class TArrayRef; + +template +using TConstArrayRef = TArrayRef; diff --git a/util/generic/guid.cpp b/util/generic/guid.cpp new file mode 100644 index 00000000000..8b907457bc6 --- /dev/null +++ b/util/generic/guid.cpp @@ -0,0 +1,186 @@ +#include "guid.h" +#include "ylimits.h" +#include "string.h" + +#include +#include +#include +#include +#include + +namespace { + inline void LowerCaseHex(TString& s) { + for (auto&& c : s) { + c = AsciiToLower(c); + } + } +} + +TString TGUID::AsGuidString() const { + TStringBuilder s; + s.reserve(50); + s << Hex(dw[0], 0) << '-' << Hex(dw[1], 0) << '-' << Hex(dw[2], 0) << '-' << Hex(dw[3], 0); + LowerCaseHex(s); + return std::move(s); +} + +TString TGUID::AsUuidString() const { + TStringBuilder s; + s.reserve(50); + s << Hex(dw[0], HF_FULL) << '-'; + s << Hex(static_cast(dw[1] >> 16), HF_FULL) << '-' << Hex(static_cast(dw[1]), HF_FULL) << '-'; + s << Hex(static_cast(dw[2] >> 16), HF_FULL) << '-' << Hex(static_cast(dw[2]), HF_FULL); + s << Hex(dw[3], HF_FULL); + LowerCaseHex(s); + return std::move(s); +} + +TGUID TGUID::Create() { + TGUID result; + CreateGuid(&result); + return result; +} + +void CreateGuid(TGUID* res) { + ui64* dw = reinterpret_cast(res->dw); + + WriteUnaligned(&dw[0], RandomNumber()); + WriteUnaligned(&dw[1], RandomNumber()); +} + +TGUID TGUID::CreateTimebased() { + TGUID result; + // GUID_EPOCH_OFFSET is the number of 100-ns intervals between the + // UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. + constexpr ui64 GUID_EPOCH_OFFSET = 0x01b21dd213814000; + const ui64 timestamp = Now().NanoSeconds() / 100 + GUID_EPOCH_OFFSET; + result.dw[0] = ui32(timestamp & 0xffffffff); // time low + const ui32 timeMid = ui32((timestamp >> 32) & 0xffff); + constexpr ui32 UUID_VERSION = 1; + const ui32 timeHighAndVersion = ui16((timestamp >> 48) & 0x0fff) | (UUID_VERSION << 12); + result.dw[1] = (timeMid << 16) | timeHighAndVersion; + const ui32 clockSeq = RandomNumber(0x3fff) | 0x8000; + result.dw[2] = (clockSeq << 16) | RandomNumber(); + result.dw[3] = RandomNumber() | (1 << 24); + return result; +} + +TString GetGuidAsString(const TGUID& g) { + return g.AsGuidString(); +} + +TString CreateGuidAsString() { + return TGUID::Create().AsGuidString(); +} + +static bool GetDigit(const char c, ui32& digit) { + digit = 0; + if ('0' <= c && c <= '9') { + digit = c - '0'; + } else if ('a' <= c && c <= 'f') { + digit = c - 'a' + 10; + } else if ('A' <= c && c <= 'F') { + digit = c - 'A' + 10; + } else { + return false; // non-hex character + } + return true; +} + +bool GetGuid(const TStringBuf s, TGUID& result) { + size_t partId = 0; + ui64 partValue = 0; + bool isEmptyPart = true; + + for (size_t i = 0; i != s.size(); ++i) { + const char c = s[i]; + + if (c == '-') { + if (isEmptyPart || partId == 3) { // x-y--z, -x-y-z or x-y-z-m-... + return false; + } + result.dw[partId] = static_cast(partValue); + ++partId; + partValue = 0; + isEmptyPart = true; + continue; + } + + ui32 digit = 0; + if (!GetDigit(c, digit)) { + return false; + } + + partValue = partValue * 16 + digit; + isEmptyPart = false; + + // overflow check + if (partValue > Max()) { + return false; + } + } + + if (partId != 3 || isEmptyPart) { // x-y or x-y-z- + return false; + } + result.dw[partId] = static_cast(partValue); + return true; +} + +// Parses GUID from s and checks that it's valid. +// In case of error returns TGUID(). +TGUID GetGuid(const TStringBuf s) { + TGUID result; + + if (GetGuid(s, result)) { + return result; + } + + return TGUID(); +} + +bool GetUuid(const TStringBuf s, TGUID& result) { + if (s.size() != 36) { + return false; + } + + size_t partId = 0; + ui64 partValue = 0; + size_t digitCount = 0; + + for (size_t i = 0; i < s.size(); ++i) { + const char c = s[i]; + + if (c == '-') { + if (i != 8 && i != 13 && i != 18 && i != 23) { + return false; + } + continue; + } + + ui32 digit = 0; + if (!GetDigit(c, digit)) { + return false; + } + + partValue = partValue * 16 + digit; + + if (++digitCount == 8) { + result.dw[partId++] = partValue; + digitCount = 0; + } + } + return true; +} + +// Parses GUID from uuid and checks that it's valid. +// In case of error returns TGUID(). +TGUID GetUuid(const TStringBuf s) { + TGUID result; + + if (GetUuid(s, result)) { + return result; + } + + return TGUID(); +} diff --git a/util/generic/guid.h b/util/generic/guid.h new file mode 100644 index 00000000000..93e06a81f87 --- /dev/null +++ b/util/generic/guid.h @@ -0,0 +1,79 @@ +#pragma once + +#include "fwd.h" + +#include + +/** + * UUID generation + * + * NOTE: It is not a real GUID (RFC 4122), as described in + * https://en.wikipedia.org/wiki/Universally_unique_identifier + * https://en.wikipedia.org/wiki/Globally_unique_identifier + * + * See https://clubs.at.yandex-team.ru/stackoverflow/10238/10240 + * and https://st.yandex-team.ru/IGNIETFERRO-768 for details. + */ +struct TGUID { + ui32 dw[4] = {}; + + constexpr bool IsEmpty() const noexcept { + return (dw[0] | dw[1] | dw[2] | dw[3]) == 0; + } + + constexpr explicit operator bool() const noexcept { + return !IsEmpty(); + } + + // xxxx-xxxx-xxxx-xxxx + TString AsGuidString() const; + + /** + * RFC4122 GUID, which described in + * https://en.wikipedia.org/wiki/Universally_unique_identifier + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + **/ + TString AsUuidString() const; + + static TGUID Create(); + + /** + * Generate time based UUID version 1 RFC4122 GUID + * https://datatracker.ietf.org/doc/html/rfc4122#section-4.1 + **/ + static TGUID CreateTimebased(); +}; + +constexpr bool operator==(const TGUID& a, const TGUID& b) noexcept { + return a.dw[0] == b.dw[0] && a.dw[1] == b.dw[1] && a.dw[2] == b.dw[2] && a.dw[3] == b.dw[3]; +} + +constexpr bool operator!=(const TGUID& a, const TGUID& b) noexcept { + return !(a == b); +} + +struct TGUIDHash { + constexpr int operator()(const TGUID& a) const noexcept { + return a.dw[0] + a.dw[1] + a.dw[2] + a.dw[3]; + } +}; + +template <> +struct THash { + constexpr size_t operator()(const TGUID& g) const noexcept { + return (unsigned int)TGUIDHash()(g); + } +}; + +void CreateGuid(TGUID* res); +TString GetGuidAsString(const TGUID& g); +TString CreateGuidAsString(); +TGUID GetGuid(TStringBuf s); +bool GetGuid(TStringBuf s, TGUID& result); + +/** + * Functions for correct parsing RFC4122 GUID, which described in + * https://en.wikipedia.org/wiki/Universally_unique_identifier + **/ +TGUID GetUuid(TStringBuf s); +bool GetUuid(TStringBuf s, TGUID& result); diff --git a/util/generic/guid_ut.cpp b/util/generic/guid_ut.cpp new file mode 100644 index 00000000000..82347941e60 --- /dev/null +++ b/util/generic/guid_ut.cpp @@ -0,0 +1,128 @@ +#include + +#include "guid.h" + +Y_UNIT_TEST_SUITE(TGuidTest) { + //TODO - make real constructor + static TGUID Construct(ui32 d1, ui32 d2, ui32 d3, ui32 d4) { + TGUID ret; + + ret.dw[0] = d1; + ret.dw[1] = d2; + ret.dw[2] = d3; + ret.dw[3] = d4; + + return ret; + } + + struct TTest { + TGUID G; + TString S; + }; + + Y_UNIT_TEST(Test1) { + for (size_t i = 0; i < 1000; ++i) { + TGUID g; + + CreateGuid(&g); + + UNIT_ASSERT_EQUAL(g, GetGuid(GetGuidAsString(g))); + } + } + + Y_UNIT_TEST(Test2) { + const TTest tests[] = { + {Construct(1, 1, 1, 1), "1-1-1-1"}, + {Construct(0, 0, 0, 0), "0-0-0-0"}, + {TGUID(), "H-0-0-0"}, + {TGUID(), "0-H-0-0"}, + {TGUID(), "0-0-H-0"}, + {TGUID(), "0-0-0-H"}, + {Construct(0x8cf813d9U, 0xc098da90U, 0x7ef58954U, 0x636d04dU), "8cf813d9-c098da90-7ef58954-636d04d"}, + {Construct(0x8cf813d9U, 0xc098da90U, 0x7ef58954U, 0x636d04dU), "8CF813D9-C098DA90-7EF58954-636D04D"}, + {Construct(0x12345678U, 0x90abcdefU, 0xfedcba09U, 0x87654321U), "12345678-90abcdef-FEDCBA09-87654321"}, + {Construct(0x1, 0x2, 0xabcdef, 0x400), "01-002-00ABCDEF-000400"}, + {TGUID(), "-1-1-1"}, // empty parts + {TGUID(), "--1-1-1"}, + {TGUID(), "1--1-1"}, + {TGUID(), "1-1"}, // unexpected end + {TGUID(), "1-1-"}, + {TGUID(), "1-1-1"}, + {TGUID(), "1-1-1-"}, + {TGUID(), "1-1-1-1-"}, + {TGUID(), "1-1-1-1-1"}, + {TGUID(), "1+1-1-1"}, // bad char + {TGUID(), "1-1:3-1-1"}, + {Construct(0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU), "FFFFFFFF-FFFFFFFF-FFFFFFFF-FFFFFFFF"}, // overflow + {TGUID(), "FFFFFFFFA-FFFFFFFF-FFFFFFFF-FFFFFFFF"}, + {TGUID(), "100000000-0-0-0"}, + {Construct(1, 1, 1, 1), "0000001-0000000000000000000000000000000000000001-0001-00000001"}, + {Construct(0, 0, 0, 0), "000000000000-000000000000000000000000000000000000000-000-0"}, + }; + + for (const auto& t : tests) { + UNIT_ASSERT_EQUAL(t.G, GetGuid(t.S)); + } + } + + Y_UNIT_TEST(Test3) { + //if this test failed, please, fix buffer size in GetGuidAsString() + TGUID max = Construct(Max(), Max(), Max(), Max()); + + UNIT_ASSERT_EQUAL(GetGuidAsString(max).length(), 35); + } + + Y_UNIT_TEST(Test4) { + UNIT_ASSERT_VALUES_EQUAL(GetGuidAsString(Construct(1, 2, 3, 4)), "1-2-3-4"); + UNIT_ASSERT_VALUES_EQUAL(GetGuidAsString(Construct(1, 2, 0xFFFFFF, 4)), "1-2-ffffff-4"); + UNIT_ASSERT_VALUES_EQUAL(GetGuidAsString(Construct(0xFAFA, 2, 3, 4)), "fafa-2-3-4"); + UNIT_ASSERT_VALUES_EQUAL(GetGuidAsString(Construct(1, 0xADE, 3, 4)), "1-ade-3-4"); + UNIT_ASSERT_VALUES_EQUAL(GetGuidAsString(Construct(1, 2, 3, 0xDEAD)), "1-2-3-dead"); + } + + Y_UNIT_TEST(Test5) { + const TTest tests[] = { + {TGUID(), "1-1-1-1-1"}, + {TGUID(), "00000001-0001-0001-0001-00000000001-"}, + {Construct(0x10000001U, 0x10011001U, 0x10011001U, 0x10000001U), "10000001-1001-1001-1001-100110000001"}, + {Construct(0x550e8400U, 0xe29b41d4U, 0xa7164466U, 0x55440000U), "550e8400-e29b-41d4-a716-446655440000"}, + {Construct(0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU), "ffffffff-ffff-ffff-ffff-ffffffffffff"}, + {TGUID(), "ffffffff-ffffff-ff-ffff-ffffffffffff"}, + {TGUID(), "ffffffff-ffff-ffff-ff-ffffffffffffff"}}; + + for (const auto& t : tests) { + UNIT_ASSERT_EQUAL(t.G, GetUuid(t.S)); + } + } + + Y_UNIT_TEST(DoubleConvert) { + /** + * test print and parsing RFC4122 GUID, which described in + * https://en.wikipedia.org/wiki/Universally_unique_identifier + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + **/ + auto guid = TGUID::Create(); + auto printed = guid.AsUuidString(); + + TGUID read; + UNIT_ASSERT(GetUuid(printed, read)); + + UNIT_ASSERT_VALUES_EQUAL(guid.dw[0], read.dw[0]); + UNIT_ASSERT_VALUES_EQUAL(guid.dw[1], read.dw[1]); + UNIT_ASSERT_VALUES_EQUAL(guid.dw[2], read.dw[2]); + UNIT_ASSERT_VALUES_EQUAL(guid.dw[3], read.dw[3]); + } + + Y_UNIT_TEST(OutputFormat) { + TGUID guid = Construct(0x00005612U, 0x12000000U, 0x00000123U, 0x00000000U); + + UNIT_ASSERT_VALUES_EQUAL(guid.AsGuidString(), "5612-12000000-123-0"); + UNIT_ASSERT_VALUES_EQUAL(guid.AsUuidString(), "00005612-1200-0000-0000-012300000000"); + } + + Y_UNIT_TEST(TimeBased) { + TString guid = TGUID::CreateTimebased().AsUuidString(); + UNIT_ASSERT(!guid.empty()); + UNIT_ASSERT_EQUAL(guid[14], '1'); + } +} diff --git a/util/generic/hash.cpp b/util/generic/hash.cpp new file mode 100644 index 00000000000..b4aac775bfe --- /dev/null +++ b/util/generic/hash.cpp @@ -0,0 +1 @@ +#include "hash.h" diff --git a/util/generic/hash.h b/util/generic/hash.h new file mode 100644 index 00000000000..8b4666f5667 --- /dev/null +++ b/util/generic/hash.h @@ -0,0 +1,355 @@ +#pragma once + +#include "fwd.h" + +#include "hash_table.h" + +template +class THashMap: public TMapOps> { +private: + using ht = THashTable, Key, HashFcn, TSelect1st, EqualKey, Alloc>; + ht rep; + +public: + using key_type = typename ht::key_type; + using value_type = typename ht::value_type; + using hasher = typename ht::hasher; + using key_equal = typename ht::key_equal; + using allocator_type = typename ht::allocator_type; + using node_allocator_type = typename ht::node_allocator_type; + using mapped_type = T; + + using size_type = typename ht::size_type; + using difference_type = typename ht::difference_type; + using pointer = typename ht::pointer; + using const_pointer = typename ht::const_pointer; + using reference = typename ht::reference; + using const_reference = typename ht::const_reference; + + using iterator = typename ht::iterator; + using const_iterator = typename ht::const_iterator; + using insert_ctx = typename ht::insert_ctx; + + hasher hash_function() const { + return rep.hash_function(); + } + key_equal key_eq() const { + return rep.key_eq(); + } + +public: + THashMap() + : rep(0, hasher(), key_equal()) + { + } + template + explicit THashMap(TAllocParam* allocParam, size_type n = 0) + : rep(n, hasher(), key_equal(), allocParam) + { + } + explicit THashMap(size_type n) + : rep(n, hasher(), key_equal()) + { + } + THashMap(size_type n, const hasher& hf) + : rep(n, hf, key_equal()) + { + } + THashMap(size_type n, const hasher& hf, const key_equal& eql) + : rep(n, hf, eql) + { + } + template + explicit THashMap(size_type n, TAllocParam* allocParam) + : rep(n, hasher(), key_equal(), allocParam) + { + } + template + THashMap(InputIterator f, InputIterator l) + : rep(0, hasher(), key_equal()) + { + rep.insert_unique(f, l); + } + template + THashMap(InputIterator f, InputIterator l, size_type n) + : rep(n, hasher(), key_equal()) + { + rep.insert_unique(f, l); + } + template + THashMap(InputIterator f, InputIterator l, size_type n, + const hasher& hf) + : rep(n, hf, key_equal()) + { + rep.insert_unique(f, l); + } + template + THashMap(InputIterator f, InputIterator l, size_type n, + const hasher& hf, const key_equal& eql) + : rep(n, hf, eql) + { + rep.insert_unique(f, l); + } + + THashMap(const std::initializer_list>& list) + : rep(list.size(), hasher(), key_equal()) + { + for (const auto& v : list) { + rep.insert_unique_noresize(v); + } + } + + // THashMap has implicit copy/move constructors and copy-/move-assignment operators + // because its implementation is backed by THashTable. + // See hash_ut.cpp + +public: + size_type size() const noexcept { + return rep.size(); + } + yssize_t ysize() const noexcept { + return (yssize_t)rep.size(); + } + size_type max_size() const noexcept { + return rep.max_size(); + } + + Y_PURE_FUNCTION bool empty() const noexcept { + return rep.empty(); + } + explicit operator bool() const noexcept { + return !empty(); + } + void swap(THashMap& hs) { + rep.swap(hs.rep); + } + + iterator begin() { + return rep.begin(); + } + iterator end() { + return rep.end(); + } + const_iterator begin() const { + return rep.begin(); + } + const_iterator end() const { + return rep.end(); + } + const_iterator cbegin() const { + return rep.begin(); + } + const_iterator cend() const { + return rep.end(); + } + +public: + template + void insert(InputIterator f, InputIterator l) { + rep.insert_unique(f, l); + } + + std::pair insert(const value_type& obj) { + return rep.insert_unique(obj); + } + + template + std::pair insert_or_assign(const Key& k, M&& value) { + auto result = try_emplace(k, std::forward(value)); + if (!result.second) { + result.first->second = std::forward(value); + } + return result; + } + + template + std::pair insert_or_assign(Key&& k, M&& value) { + auto result = try_emplace(std::move(k), std::forward(value)); + if (!result.second) { + result.first->second = std::forward(value); + } + return result; + } + + template + std::pair emplace(Args&&... args) { + return rep.emplace_unique(std::forward(args)...); + } + + std::pair insert_noresize(const value_type& obj) { + return rep.insert_unique_noresize(obj); + } + + template + std::pair emplace_noresize(Args&&... args) { + return rep.emplace_unique_noresize(std::forward(args)...); + } + + template + iterator insert_direct(const TheObj& obj, const insert_ctx& ins) { + return rep.insert_direct(obj, ins); + } + + template + iterator emplace_direct(const insert_ctx& ins, Args&&... args) { + return rep.emplace_direct(ins, std::forward(args)...); + } + + template + std::pair try_emplace(TKey&& key, Args&&... args) { + insert_ctx ctx = nullptr; + iterator it = find(key, ctx); + if (it == end()) { + it = rep.emplace_direct(ctx, std::piecewise_construct, + std::forward_as_tuple(std::forward(key)), + std::forward_as_tuple(std::forward(args)...)); + return {it, true}; + } + return {it, false}; + } + + template + iterator find(const TheKey& key) { + return rep.find(key); + } + + template + const_iterator find(const TheKey& key) const { + return rep.find(key); + } + + template + iterator find(const TheKey& key, insert_ctx& ins) { + return rep.find_i(key, ins); + } + + template + bool contains(const TheKey& key) const { + return rep.find(key) != rep.end(); + } + bool contains(const key_type& key) const { + return rep.find(key) != rep.end(); + } + + template + bool contains(const TheKey& key, insert_ctx& ins) { + return rep.find_i(key, ins) != rep.end(); + } + + template + T& operator[](const TKey& key) { + insert_ctx ctx = nullptr; + iterator it = find(key, ctx); + + if (it != end()) { + return it->second; + } + + return rep.emplace_direct(ctx, std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple())->second; + } + + template + const T& at(const TheKey& key) const { + using namespace ::NPrivate; + const_iterator it = find(key); + + if (Y_UNLIKELY(it == end())) { + ::NPrivate::ThrowKeyNotFoundInHashTableException(MapKeyToString(key)); + } + + return it->second; + } + + template + T& at(const TheKey& key) { + using namespace ::NPrivate; + iterator it = find(key); + + if (Y_UNLIKELY(it == end())) { + ::NPrivate::ThrowKeyNotFoundInHashTableException(MapKeyToString(key)); + } + + return it->second; + } + + template + size_type count(const TKey& key) const { + return rep.count(key); + } + + template + std::pair equal_range(const TKey& key) { + return rep.equal_range(key); + } + + template + std::pair equal_range(const TKey& key) const { + return rep.equal_range(key); + } + + template + size_type erase(const TKey& key) { + return rep.erase_one(key); + } + + void erase(iterator it) { + rep.erase(it); + } + void erase(iterator f, iterator l) { + rep.erase(f, l); + } + Y_REINITIALIZES_OBJECT void clear() { + rep.clear(); + } + Y_REINITIALIZES_OBJECT void clear(size_t downsize_hint) { + rep.clear(downsize_hint); + } + Y_REINITIALIZES_OBJECT void basic_clear() { + rep.basic_clear(); + } + void release_nodes() { + rep.release_nodes(); + } + + // if (stHash != NULL) bucket_count() must be equal to stHash->bucket_count() + template + int save_for_st(IOutputStream* stream, KeySaver& ks, sthash, TEqualTo, typename KeySaver::TSizeType>* stHash = nullptr) const { + return rep.template save_for_st(stream, ks, stHash); + } + +public: + void reserve(size_type hint) { + rep.reserve(hint); + } + size_type bucket_count() const { + return rep.bucket_count(); + } + size_type bucket_size(size_type n) const { + return rep.bucket_size(n); + } + node_allocator_type& GetNodeAllocator() { + return rep.GetNodeAllocator(); + } + const node_allocator_type& GetNodeAllocator() const { + return rep.GetNodeAllocator(); + } +}; + +template +inline bool operator==(const THashMap& hm1, const THashMap& hm2) { + if (hm1.size() != hm2.size()) { + return false; + } + for (const auto& it1 : hm1) { + auto it2 = hm2.find(it1.first); + if ((it2 == hm2.end()) || !(it1 == *it2)) { + return false; + } + } + return true; +} + +template +inline bool operator!=(const THashMap& hm1, const THashMap& hm2) { + return !(hm1 == hm2); +} diff --git a/util/generic/hash.pxd b/util/generic/hash.pxd new file mode 100644 index 00000000000..385c10d805f --- /dev/null +++ b/util/generic/hash.pxd @@ -0,0 +1,66 @@ +from libcpp.pair cimport pair + +cdef extern from "util/generic/hash.h" nogil: + cdef cppclass THashMap[T, U]: + cppclass iterator: + pair[T, U]& operator*() + iterator operator++() + iterator operator--() + bint operator==(iterator) + bint operator!=(iterator) + + cppclass const_iterator(iterator): + pass + + cppclass reverse_iterator: + pair[T, U]& operator*() + iterator operator++() + iterator operator--() + bint operator==(reverse_iterator) + bint operator!=(reverse_iterator) + + cppclass const_reverse_iterator(reverse_iterator): + pass + + THashMap() except + + THashMap(THashMap&) except + + U& operator[](T&) + THashMap& operator=(THashMap&) + + bint operator==(THashMap&) + bint operator!=(THashMap&) + bint operator<(THashMap&) + bint operator>(THashMap&) + bint operator<=(THashMap&) + bint operator>=(THashMap&) + + U& at(T&) except + + iterator begin() + const_iterator const_begin "begin"() + void clear() + size_t count(T&) + bint empty() + iterator end() + const_iterator const_end "end"() + pair[iterator, iterator] equal_range(T&) + void erase(iterator) except + + void erase(iterator, iterator) except + + size_t erase(T&) + iterator find(T&) + bint contains(T&) + const_iterator const_find "find"(T&) + pair[iterator, bint] insert(pair[T, U]) # XXX pair[T,U]& + iterator insert(iterator, pair[T, U]) # XXX pair[T,U]& + size_t max_size() + size_t size() + void swap(THashMap&) + iterator lower_bound(T&) + const_iterator const_lower_bound "lower_bound"(T&) + reverse_iterator rbegin() + const_reverse_iterator const_rbegin "rbegin"() + reverse_iterator rend() + const_reverse_iterator const_rend "rend"() + iterator upper_bound(T&) + const_iterator const_upper_bound "upper_bound"(T&) + void max_load_factor(float) + float max_load_factor() diff --git a/util/generic/hash_multi_map.cpp b/util/generic/hash_multi_map.cpp new file mode 100644 index 00000000000..f8dcf6e4dc5 --- /dev/null +++ b/util/generic/hash_multi_map.cpp @@ -0,0 +1 @@ +#include "hash_multi_map.h" diff --git a/util/generic/hash_multi_map.h b/util/generic/hash_multi_map.h new file mode 100644 index 00000000000..6b027073cb0 --- /dev/null +++ b/util/generic/hash_multi_map.h @@ -0,0 +1,278 @@ +#pragma once + +#include "fwd.h" +#include "hash_table.h" + +template +class THashMultiMap { +private: + using ht = THashTable, Key, HashFcn, TSelect1st, EqualKey, Alloc>; + ht rep; + +public: + using key_type = typename ht::key_type; + using value_type = typename ht::value_type; + using hasher = typename ht::hasher; + using key_equal = typename ht::key_equal; + using mapped_type = T; + using allocator_type = typename ht::allocator_type; + + using size_type = typename ht::size_type; + using difference_type = typename ht::difference_type; + using pointer = typename ht::pointer; + using const_pointer = typename ht::const_pointer; + using reference = typename ht::reference; + using const_reference = typename ht::const_reference; + + using iterator = typename ht::iterator; + using const_iterator = typename ht::const_iterator; + using insert_ctx = typename ht::insert_ctx; + + hasher hash_function() const { + return rep.hash_function(); + } + key_equal key_eq() const { + return rep.key_eq(); + } + +public: + THashMultiMap() + : rep(0, hasher(), key_equal()) + { + } + template + explicit THashMultiMap(TAllocParam* allocParam) + : rep(0, hasher(), key_equal(), allocParam) + { + } + explicit THashMultiMap(size_type n) + : rep(n, hasher(), key_equal()) + { + } + THashMultiMap(size_type n, const hasher& hf) + : rep(n, hf, key_equal()) + { + } + THashMultiMap(size_type n, const hasher& hf, const key_equal& eql) + : rep(n, hf, eql) + { + } + + template + THashMultiMap(InputIterator f, InputIterator l) + : rep(0, hasher(), key_equal()) + { + rep.insert_equal(f, l); + } + template + THashMultiMap(InputIterator f, InputIterator l, size_type n) + : rep(n, hasher(), key_equal()) + { + rep.insert_equal(f, l); + } + template + THashMultiMap(InputIterator f, InputIterator l, size_type n, const hasher& hf) + : rep(n, hf, key_equal()) + { + rep.insert_equal(f, l); + } + template + THashMultiMap(InputIterator f, InputIterator l, size_type n, const hasher& hf, const key_equal& eql) + : rep(n, hf, eql) + { + rep.insert_equal(f, l); + } + + THashMultiMap(std::initializer_list> list) + : rep(list.size(), hasher(), key_equal()) + { + for (const auto& v : list) { + rep.emplace_equal_noresize(v); + } + } + + // THashMultiMap has implicit copy/move constructors and copy-/move-assignment operators + // because its implementation is backed by THashTable. + // See hash_ut.cpp + +public: + size_type size() const { + return rep.size(); + } + yssize_t ysize() const { + return (yssize_t)rep.size(); + } + size_type max_size() const { + return rep.max_size(); + } + + Y_PURE_FUNCTION bool empty() const { + return rep.empty(); + } + explicit operator bool() const noexcept { + return !empty(); + } + void swap(THashMultiMap& hs) { + rep.swap(hs.rep); + } + + iterator begin() { + return rep.begin(); + } + iterator end() { + return rep.end(); + } + const_iterator begin() const { + return rep.begin(); + } + const_iterator end() const { + return rep.end(); + } + const_iterator cbegin() const { + return rep.begin(); + } + const_iterator cend() const { + return rep.end(); + } + +public: + template + void insert(InputIterator f, InputIterator l) { + rep.insert_equal(f, l); + } + + iterator insert(const value_type& obj) { + return rep.insert_equal(obj); + } + + template + iterator emplace(Args&&... args) { + return rep.emplace_equal(std::forward(args)...); + } + + iterator insert_noresize(const value_type& obj) { + return rep.emplace_equal_noresize(obj); + } + + template + iterator emplace_noresize(Args&&... args) { + return rep.emplace_equal_noresize(std::forward(args)...); + } + + template + iterator insert_direct(const TheObj& obj, const insert_ctx& ins) { + return rep.insert_direct(obj, ins); + } + + template + iterator emplace_direct(const insert_ctx& ins, Args&&... args) { + return rep.emplace_direct(ins, std::forward(args)...); + } + + template + const_iterator find(const TKey& key) const { + return rep.find(key); + } + + template + iterator find(const TKey& key) { + return rep.find(key); + } + + template + iterator find(const TheKey& key, insert_ctx& ins) { + return rep.find_i(key, ins); + } + + template + bool contains(const TheKey& key) const { + return rep.find(key) != rep.end(); + } + + template + size_type count(const TKey& key) const { + return rep.count(key); + } + + template + std::pair equal_range(const TKey& key) { + return rep.equal_range(key); + } + + template + std::pair equal_range_i(const TKey& key, insert_ctx& ins) { + return rep.equal_range_i(key, ins); + } + + template + std::pair equal_range(const TKey& key) const { + return rep.equal_range(key); + } + + size_type erase(const key_type& key) { + return rep.erase(key); + } + void erase(iterator it) { + rep.erase(it); + } + void erase(iterator f, iterator l) { + rep.erase(f, l); + } + Y_REINITIALIZES_OBJECT void clear() { + rep.clear(); + } + Y_REINITIALIZES_OBJECT void clear(size_t downsize_hint) { + rep.clear(downsize_hint); + } + Y_REINITIALIZES_OBJECT void basic_clear() { + rep.basic_clear(); + } + void release_nodes() { + rep.release_nodes(); + } + + // if (stHash != NULL) bucket_count() must be equal to stHash->bucket_count() + template + int save_for_st(IOutputStream* stream, KeySaver& ks, sthash, TEqualTo, typename KeySaver::TSizeType>* stHash = nullptr) const { + return rep.template save_for_st(stream, ks, stHash); + } + +public: + void reserve(size_type hint) { + rep.reserve(hint); + } + size_type bucket_count() const { + return rep.bucket_count(); + } + size_type bucket_size(size_type n) const { + return rep.bucket_size(n); + } +}; + +template +inline bool operator==(const THashMultiMap& hm1, const THashMultiMap& hm2) { + // NOTE: copy-pasted from + // contrib/libs/cxxsupp/libcxx/include/unordered_map + // and adapted to THashMultiMap + if (hm1.size() != hm2.size()) { + return false; + } + using const_iterator = typename THashMultiMap::const_iterator; + using TEqualRange = std::pair; + for (const_iterator it = hm1.begin(), end = hm1.end(); it != end;) { + TEqualRange eq1 = hm1.equal_range(it->first); + TEqualRange eq2 = hm2.equal_range(it->first); + if (std::distance(eq1.first, eq1.second) != std::distance(eq2.first, eq2.second) || + !std::is_permutation(eq1.first, eq1.second, eq2.first)) + { + return false; + } + it = eq1.second; + } + return true; +} + +template +inline bool operator!=(const THashMultiMap& hm1, const THashMultiMap& hm2) { + return !(hm1 == hm2); +} diff --git a/util/generic/hash_primes.cpp b/util/generic/hash_primes.cpp new file mode 100644 index 00000000000..656d31e0464 --- /dev/null +++ b/util/generic/hash_primes.cpp @@ -0,0 +1,121 @@ +#include "hash_primes.h" +#include "array_size.h" +#include "algorithm.h" + +/// Order of fields: reciprocal, reciprocal shift, adjacent hint, divisor +#if defined(_32_) +static constexpr ::NPrivate::THashDivisor PRIME_DIVISORS_HOLDER[]{ + {0x00000000u, 0u, -1, 0xffffffffu}, // guard value, not a valid divisor + {0x24924925u, 2u, 0, 7u}, + {0xe1e1e1e2u, 4u, 1, 17u}, + {0x1a7b9612u, 4u, 2, 29u}, + {0x3521cfb3u, 5u, 3, 53u}, + {0x51d07eafu, 6u, 4, 97u}, + {0x53909490u, 7u, 5, 193u}, + {0x50f22e12u, 8u, 6, 389u}, + {0x54e3b41au, 9u, 7, 769u}, + {0x53c8eaeeu, 10u, 8, 1543u}, + {0x548eacc6u, 11u, 9, 3079u}, + {0x54f1e41eu, 12u, 10, 6151u}, + {0x554e390au, 13u, 11, 12289u}, + {0x5518ee41u, 14u, 12, 24593u}, + {0x554c7203u, 15u, 13, 49157u}, + {0x5549c781u, 16u, 14, 98317u}, + {0x55531c76u, 17u, 15, 196613u}, + {0x554fc734u, 18u, 16, 393241u}, + {0x555538e4u, 19u, 17, 786433u}, + {0x55550e39u, 20u, 18, 1572869u}, + {0x5555071du, 21u, 19, 3145739u}, + {0x5555271du, 22u, 20, 6291469u}, + {0x55554c72u, 23u, 21, 12582917u}, + {0x55554472u, 24u, 22, 25165843u}, + {0x5555531du, 25u, 23, 50331653u}, + {0x55555039u, 26u, 24, 100663319u}, + {0x55555339u, 27u, 25, 201326611u}, + {0x5555550fu, 28u, 26, 402653189u}, + {0x555552ddu, 29u, 27, 805306457u}, + {0x55555544u, 30u, 28, 1610612741u}, + {0x55555554u, 31u, 29, 3221225473u}, + {0x00000006u, 31u, 30, 4294967291u}, +}; +#else +static constexpr ::NPrivate::THashDivisor PRIME_DIVISORS_HOLDER[]{ + {0x0000000000000000ul, 0u, -1, 0xffffffffu}, // guard value, not a valid divisor + {0x2492492492492493ul, 2u, 0, 7u}, + {0xe1e1e1e1e1e1e1e2ul, 4u, 1, 17u}, + {0x1a7b9611a7b9611bul, 4u, 2, 29u}, + {0x3521cfb2b78c1353ul, 5u, 3, 53u}, + {0x51d07eae2f8151d1ul, 6u, 4, 97u}, + {0x5390948f40feac70ul, 7u, 5, 193u}, + {0x50f22e111c4c56dful, 8u, 6, 389u}, + {0x54e3b4194ce65de1ul, 9u, 7, 769u}, + {0x53c8eaedea6e7f17ul, 10u, 8, 1543u}, + {0x548eacc5e1e6e3fcul, 11u, 9, 3079u}, + {0x54f1e41d7767d70cul, 12u, 10, 6151u}, + {0x554e39097a781d80ul, 13u, 11, 12289u}, + {0x5518ee4079ea6929ul, 14u, 12, 24593u}, + {0x554c72025d459231ul, 15u, 13, 49157u}, + {0x5549c78094504ff3ul, 16u, 14, 98317u}, + {0x55531c757b3c329cul, 17u, 15, 196613u}, + {0x554fc7339753b424ul, 18u, 16, 393241u}, + {0x555538e39097b3f4ul, 19u, 17, 786433u}, + {0x55550e38f25ecd82ul, 20u, 18, 1572869u}, + {0x5555071c83b421d2ul, 21u, 19, 3145739u}, + {0x5555271c78097a6aul, 22u, 20, 6291469u}, + {0x55554c71c757b425ul, 23u, 21, 12582917u}, + {0x55554471c7f25ec7ul, 24u, 22, 25165843u}, + {0x5555531c71cad098ul, 25u, 23, 50331653u}, + {0x55555038e3a1d098ul, 26u, 24, 100663319u}, + {0x55555338e3919098ul, 27u, 25, 201326611u}, + {0x5555550e38e39d0aul, 28u, 26, 402653189u}, + {0x555552dc71cbb1eeul, 29u, 27, 805306457u}, + {0x555555438e38e47cul, 30u, 28, 1610612741u}, + {0x555555538e38e391ul, 31u, 29, 3221225473u}, + {0x000000050000001aul, 31u, 30, 4294967291u}, +}; +#endif + +static constexpr const ::NPrivate::THashDivisor* PRIME_DIVISORS = &PRIME_DIVISORS_HOLDER[1]; ///< Address of the first valid divisor +static constexpr size_t PRIME_DIVISORS_SIZE = Y_ARRAY_SIZE(PRIME_DIVISORS_HOLDER) - 1; ///< Number of valid divisors without the guarding value + +unsigned long HashBucketCount(unsigned long elementCount) { + return HashBucketCountExt(elementCount)(); +} + +static inline ::NPrivate::THashDivisor HashBucketBoundedSearch(unsigned long elementCount) { + const auto begin = PRIME_DIVISORS; + const auto end = PRIME_DIVISORS + PRIME_DIVISORS_SIZE - 1; // adjust range so the last element will be returned if elementCount is bigger than all PRIME_DIVISORS + return *LowerBoundBy(begin, end, elementCount, std::mem_fn(&::NPrivate::THashDivisor::Divisor)); +} + +Y_CONST_FUNCTION +::NPrivate::THashDivisor HashBucketCountExt(unsigned long elementCount) { + if (elementCount <= PRIME_DIVISORS[0]()) { + return PRIME_DIVISORS[0]; + } + + return HashBucketBoundedSearch(elementCount); +} + +Y_CONST_FUNCTION +::NPrivate::THashDivisor HashBucketCountExt(unsigned long elementCount, int hint) { + if (Y_LIKELY(static_cast(hint) < PRIME_DIVISORS_SIZE)) { + const int index = hint; + const ::NPrivate::THashDivisor* cnd = PRIME_DIVISORS + index; + if (Y_LIKELY(elementCount <= cnd->Divisor)) { + const ::NPrivate::THashDivisor* prev = cnd - 1; + static_assert(~PRIME_DIVISORS[-1].Divisor == 0, "Invalid guard"); + /* + If index == 0 then PRIME_DIVISORS[0] should be returned. + Otherwise `cnd` is correct value iff (prev->Divisor < elementCount). + Ergo hint is correct if (index == 0 || prev->Divisor < elementCount); + But we can set guard's value to -1 and check both conditions at once. + */ + if (Y_LIKELY(prev->Divisor + 1u <= elementCount)) { + return *cnd; + } + } + } + + return HashBucketBoundedSearch(elementCount); +} diff --git a/util/generic/hash_primes.h b/util/generic/hash_primes.h new file mode 100644 index 00000000000..4dc2da0b8f0 --- /dev/null +++ b/util/generic/hash_primes.h @@ -0,0 +1,138 @@ +#pragma once + +#include +#include + +#if defined(_MSC_VER) && defined(_M_X64) + #include +#endif + +/** + * Calculates the number of buckets for the hash table that will hold the given + * number of elements. + * + * @param elementCount Number of elements that the hash table will hold. + * @returns Number of buckets, a prime number that is + * greater or equal to `elementCount`. + */ +Y_CONST_FUNCTION +unsigned long HashBucketCount(unsigned long elementCount); + +namespace NPrivate { + + /// Implementation of algorithm 4.1 from: Torbjörn Granlund and Peter L. Montgomery. 1994. Division by invariant integers using multiplication. + /// @see https://gmplib.org/~tege/divcnst-pldi94.pdf + template + class TReciprocalDivisor { + static_assert(sizeof(TDivisor) <= sizeof(TDividend), "TDivisor and TDividend should have the same size"); + + public: + constexpr TReciprocalDivisor() noexcept = default; + + constexpr TReciprocalDivisor(TDividend reciprocal, ui8 reciprocalShift, i8 hint, TDivisor divisor) noexcept + : Reciprocal(reciprocal) + , Divisor(divisor) + , ReciprocalShift(reciprocalShift) + , Hint(hint) + { + } + + /// Return (dividend % Divisor) + Y_FORCE_INLINE TDividend Remainder(TDividend dividend) const noexcept { + if (Y_UNLIKELY(Divisor == 1)) { + return 0; + } + TDividend r = dividend - Quotent(dividend) * Divisor; + return r; + } + + Y_FORCE_INLINE TDivisor operator()() const noexcept { + return Divisor; + } + + Y_FORCE_INLINE static constexpr TReciprocalDivisor One() noexcept { + return {1u, 0u, -1, 1u}; + } + + private: + /// returns (dividend / Divisor) + Y_FORCE_INLINE TDividend Quotent(TDividend dividend) const noexcept { + const TDividend t = MulUnsignedUpper{}(dividend, Reciprocal); + const TDividend q = (t + ((dividend - t) >> 1)) >> ReciprocalShift; + return q; + } + + public: + TDividend Reciprocal = 0; + TDivisor Divisor = 0; + ui8 ReciprocalShift = 0; + i8 Hint = 0; ///< Additional data: needless for division, but useful for the adjacent divisors search + }; + + template + struct TMulUnsignedUpper { + /// Return the high bits of the product of two unsigned integers. + Y_FORCE_INLINE T operator()(T a, T b) const noexcept { + return (static_cast(a) * static_cast(b)) >> shift; + } + }; + +#if defined(_32_) + using THashDivisor = ::NPrivate::TReciprocalDivisor>; +#else + #if defined(Y_HAVE_INT128) + using THashDivisor = ::NPrivate::TReciprocalDivisor>; + #elif defined(_MSC_VER) && defined(_M_X64) + struct TMulUnsignedUpperVCIntrin { + /// Return the high 64 bits of the product of two 64-bit unsigned integers. + Y_FORCE_INLINE ui64 operator()(ui64 a, ui64 b) const noexcept { + return __umulh(a, b); + } + }; + using THashDivisor = ::NPrivate::TReciprocalDivisor; + #else + template + class TNaiveDivisor { + public: + constexpr TNaiveDivisor() noexcept = default; + + constexpr TNaiveDivisor(TDivisor divisor) noexcept + : Divisor(divisor) + { + } + + constexpr TNaiveDivisor(TDividend reciprocal, ui8 reciprocalShift, i8 hint, TDivisor divisor) noexcept + : TNaiveDivisor(divisor) + { + Y_UNUSED(reciprocal); + Y_UNUSED(reciprocalShift); + Y_UNUSED(hint); + } + + Y_FORCE_INLINE TDividend Remainder(TDividend dividend) const noexcept { + return dividend % Divisor; + } + + Y_FORCE_INLINE TDivisor operator()() const noexcept { + return Divisor; + } + + Y_FORCE_INLINE static constexpr TNaiveDivisor One() noexcept { + return {1u}; + } + + public: + TDivisor Divisor = 0; + static constexpr i8 Hint = -1; + }; + + using THashDivisor = ::NPrivate::TNaiveDivisor; + #endif +#endif +} + +Y_CONST_FUNCTION +::NPrivate::THashDivisor HashBucketCountExt(unsigned long elementCount); + +Y_CONST_FUNCTION +::NPrivate::THashDivisor HashBucketCountExt(unsigned long elementCount, int hint); diff --git a/util/generic/hash_primes_ut.cpp b/util/generic/hash_primes_ut.cpp new file mode 100644 index 00000000000..7b5bf8b5c9a --- /dev/null +++ b/util/generic/hash_primes_ut.cpp @@ -0,0 +1,80 @@ +#include "hash_primes.h" + +#include + +#include +#include +#include + +Y_UNIT_TEST_SUITE(TestHashPrimes) { + Y_UNIT_TEST(Test1) { + UNIT_ASSERT_VALUES_EQUAL(HashBucketCount(1), 7); + UNIT_ASSERT_VALUES_EQUAL(HashBucketCount(6), 7); + UNIT_ASSERT_VALUES_EQUAL(HashBucketCount(7), 7); + UNIT_ASSERT_VALUES_EQUAL(HashBucketCount(8), 17); + } + + static TVector Numbers() { + TVector numbers; + + TFastRng64 rng{961923}; + size_t k = 1; + for (size_t j = 0; j < 8000; ++j) { + numbers.push_back(rng.GenRand()); + numbers.push_back(k *= 57); + } + for (size_t p = 1; p != 0; p <<= 1) { + for (size_t offset : {-2, -1, 0, 1, 2}) { + numbers.push_back(p + offset); + } + } + return numbers; + } + + static TVector Divisors() { + TVector divisors; + divisors.push_back(HashBucketCountExt(0)()); + for (;;) { + const size_t prevSize = divisors.back(); + const size_t nextSize = HashBucketCountExt(prevSize + 1)(); + if (nextSize <= prevSize) { + break; + } + divisors.push_back(nextSize); + } + return divisors; + } + + Y_UNIT_TEST(Remainder) { + const TVector numbers = Numbers(); + const TVector divisors = Divisors(); + + auto testDivisor = [&](const auto& c) { + for (size_t n : numbers) { + UNIT_ASSERT_VALUES_EQUAL_C(n % c(), c.Remainder(n), (TStringBuilder() << "n=" << n << "; d=" << c())); + } + }; + + for (size_t d : divisors) { + const auto c = HashBucketCountExt(d); + UNIT_ASSERT_VALUES_EQUAL_C(d, c(), (TStringBuilder() << "d=" << d)); + testDivisor(c); + } + testDivisor(::NPrivate::THashDivisor::One()); + } + + Y_UNIT_TEST(MisleadingHints) { + TFastRng64 rng{332142}; + TVector cases = Numbers(); + for (size_t d : Divisors()) { + cases.push_back(d); + } + + for (size_t c : cases) { + for (size_t reps = 0; reps < 3; ++reps) { + const i8 hint = rng.Uniform(256) - 128; + UNIT_ASSERT_VALUES_EQUAL_C(HashBucketCountExt(c)(), HashBucketCountExt(c, hint)(), (TStringBuilder() << "c=" << c << "; hint=" << hint)); + } + } + } +} diff --git a/util/generic/hash_set.cpp b/util/generic/hash_set.cpp new file mode 100644 index 00000000000..8fabeeabd87 --- /dev/null +++ b/util/generic/hash_set.cpp @@ -0,0 +1 @@ +#include "hash_set.h" diff --git a/util/generic/hash_set.h b/util/generic/hash_set.h new file mode 100644 index 00000000000..cb86b72106e --- /dev/null +++ b/util/generic/hash_set.h @@ -0,0 +1,490 @@ +#pragma once + +#include "fwd.h" +#include "hash.h" + +#include + +#include +#include + +#undef value_type + +template +class THashSet { +private: + using ht = THashTable; + ht rep; + + using mutable_iterator = typename ht::iterator; + +public: + using key_type = typename ht::key_type; + using value_type = typename ht::value_type; + using hasher = typename ht::hasher; + using key_equal = typename ht::key_equal; + using allocator_type = typename ht::allocator_type; + using node_allocator_type = typename ht::node_allocator_type; + + using size_type = typename ht::size_type; + using difference_type = typename ht::difference_type; + using pointer = typename ht::const_pointer; + using const_pointer = typename ht::const_pointer; + using reference = typename ht::const_reference; + using const_reference = typename ht::const_reference; + + using iterator = typename ht::const_iterator; + using const_iterator = typename ht::const_iterator; + using insert_ctx = typename ht::insert_ctx; + + hasher hash_function() const { + return rep.hash_function(); + } + key_equal key_eq() const { + return rep.key_eq(); + } + +public: + THashSet() { + } + template + explicit THashSet(TT* allocParam, size_type n = 0) + : rep(n, hasher(), key_equal(), allocParam) + { + } + explicit THashSet(size_type n) + : rep(n, hasher(), key_equal()) + { + } + THashSet(size_type n, const hasher& hf) + : rep(n, hf, key_equal()) + { + } + THashSet(size_type n, const hasher& hf, const key_equal& eql) + : rep(n, hf, eql) + { + } + + THashSet(std::initializer_list list) + : rep(list.size(), hasher(), key_equal()) + { + rep.insert_unique(list.begin(), list.end()); + } + THashSet(std::initializer_list list, size_type n) + : rep(n, hasher(), key_equal()) + { + rep.insert_unique(list.begin(), list.end()); + } + THashSet(std::initializer_list list, size_type n, const hasher& hf) + : rep(n, hf, key_equal()) + { + rep.insert_unique(list.begin(), list.end()); + } + THashSet(std::initializer_list list, size_type n, const hasher& hf, const key_equal& eql) + : rep(n, hf, eql) + { + rep.insert_unique(list.begin(), list.end()); + } + + template + THashSet(InputIterator f, InputIterator l) + : rep(0, hasher(), key_equal()) + { + rep.insert_unique(f, l); + } + template + THashSet(InputIterator f, InputIterator l, size_type n) + : rep(n, hasher(), key_equal()) + { + rep.insert_unique(f, l); + } + template + THashSet(InputIterator f, InputIterator l, size_type n, + const hasher& hf) + : rep(n, hf, key_equal()) + { + rep.insert_unique(f, l); + } + template + THashSet(InputIterator f, InputIterator l, size_type n, + const hasher& hf, const key_equal& eql) + : rep(n, hf, eql) + { + rep.insert_unique(f, l); + } + + // THashSet has implicit copy/move constructors and copy-/move-assignment operators + // because its implementation is backed by THashTable. + // See hash_ut.cpp + +public: + size_type size() const { + return rep.size(); + } + size_type max_size() const { + return rep.max_size(); + } + + Y_PURE_FUNCTION bool empty() const { + return rep.empty(); + } + explicit operator bool() const noexcept { + return !empty(); + } + void swap(THashSet& hs) { + rep.swap(hs.rep); + } + + iterator begin() const { + return rep.begin(); + } + iterator end() const { + return rep.end(); + } + iterator cbegin() const { + return rep.begin(); + } + iterator cend() const { + return rep.end(); + } + +public: + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + template + void insert(InputIterator f, InputIterator l) { + rep.insert_unique(f, l); + } + + std::pair insert(const value_type& obj) { + std::pair p = rep.insert_unique(obj); + return std::pair(p.first, p.second); + } + template + std::pair emplace(Args&&... args) { + std::pair p = rep.emplace_unique(std::forward(args)...); + return std::pair(p.first, p.second); + } + + iterator insert(const_iterator, const value_type& obj) { // insert_hint + std::pair p = rep.insert_unique(obj); + return p.first; + } + + std::pair insert_noresize(const value_type& obj) { + std::pair p = rep.insert_unique_noresize(obj); + return std::pair(p.first, p.second); + } + template + std::pair emplace_noresize(Args&&... args) { + std::pair p = rep.emplace_unique_noresize(std::forward(args)...); + return std::pair(p.first, p.second); + } + + template + iterator insert_direct(const TheObj& obj, const insert_ctx& ins) { + return rep.insert_direct(obj, ins); + } + template + iterator emplace_direct(const insert_ctx& ins, Args&&... args) { + return rep.emplace_direct(ins, std::forward(args)...); + } + + template + const_iterator find(const TheKey& key) const { + return rep.find(key); + } + template + iterator find(const TheKey& key, insert_ctx& ins) { + return rep.find_i(key, ins); + } + + template + bool contains(const TheKey& key) const { + return rep.find(key) != rep.end(); + } + template + bool contains(const TheKey& key, insert_ctx& ins) { + return rep.find_i(key, ins) != rep.end(); + } + + template + size_type count(const TKey& key) const { + return rep.count(key); + } + + template + std::pair equal_range(const TKey& key) const { + return rep.equal_range(key); + } + + size_type erase(const key_type& key) { + return rep.erase_one(key); + } + void erase(iterator it) { + rep.erase(it); + } + void erase(iterator f, iterator l) { + rep.erase(f, l); + } + Y_REINITIALIZES_OBJECT void clear() { + rep.clear(); + } + Y_REINITIALIZES_OBJECT void clear(size_t downsize_hint) { + rep.clear(downsize_hint); + } + Y_REINITIALIZES_OBJECT void basic_clear() { + rep.basic_clear(); + } + void release_nodes() { + rep.release_nodes(); + } + + template + int save_for_st(IOutputStream* stream, KeySaver& ks) const { + return rep.template save_for_st(stream, ks); + } + +public: + void reserve(size_type hint) { + rep.reserve(hint); + } + size_type bucket_count() const { + return rep.bucket_count(); + } + size_type bucket_size(size_type n) const { + return rep.bucket_size(n); + } + node_allocator_type& GetNodeAllocator() { + return rep.GetNodeAllocator(); + } +}; + +template +inline bool operator==(const THashSet& hs1, const THashSet& hs2) { + if (hs1.size() != hs2.size()) { + return false; + } + for (const auto& it : hs1) { + if (!hs2.contains(it)) { + return false; + } + } + return true; +} + +template +inline bool operator!=(const THashSet& hs1, const THashSet& hs2) { + return !(hs1 == hs2); +} + +template +class THashMultiSet { +private: + using ht = THashTable; + ht rep; + +public: + using key_type = typename ht::key_type; + using value_type = typename ht::value_type; + using hasher = typename ht::hasher; + using key_equal = typename ht::key_equal; + using allocator_type = typename ht::allocator_type; + using node_allocator_type = typename ht::node_allocator_type; + + using size_type = typename ht::size_type; + using difference_type = typename ht::difference_type; + using pointer = typename ht::const_pointer; + using const_pointer = typename ht::const_pointer; + using reference = typename ht::const_reference; + using const_reference = typename ht::const_reference; + + using iterator = typename ht::const_iterator; + using const_iterator = typename ht::const_iterator; + + hasher hash_function() const { + return rep.hash_function(); + } + key_equal key_eq() const { + return rep.key_eq(); + } + +public: + THashMultiSet() + : rep(0, hasher(), key_equal()) + { + } + explicit THashMultiSet(size_type n) + : rep(n, hasher(), key_equal()) + { + } + THashMultiSet(size_type n, const hasher& hf) + : rep(n, hf, key_equal()) + { + } + THashMultiSet(size_type n, const hasher& hf, const key_equal& eql) + : rep(n, hf, eql) + { + } + + template + THashMultiSet(InputIterator f, InputIterator l) + : rep(0, hasher(), key_equal()) + { + rep.insert_equal(f, l); + } + template + THashMultiSet(InputIterator f, InputIterator l, size_type n) + : rep(n, hasher(), key_equal()) + { + rep.insert_equal(f, l); + } + template + THashMultiSet(InputIterator f, InputIterator l, size_type n, + const hasher& hf) + : rep(n, hf, key_equal()) + { + rep.insert_equal(f, l); + } + template + THashMultiSet(InputIterator f, InputIterator l, size_type n, + const hasher& hf, const key_equal& eql) + : rep(n, hf, eql) + { + rep.insert_equal(f, l); + } + + THashMultiSet(std::initializer_list list) + : rep(list.size(), hasher(), key_equal()) + { + rep.insert_equal(list.begin(), list.end()); + } + + // THashMultiSet has implicit copy/move constructors and copy-/move-assignment operators + // because its implementation is backed by THashTable. + // See hash_ut.cpp + +public: + size_type size() const { + return rep.size(); + } + size_type max_size() const { + return rep.max_size(); + } + + Y_PURE_FUNCTION bool empty() const { + return rep.empty(); + } + explicit operator bool() const noexcept { + return !empty(); + } + void swap(THashMultiSet& hs) { + rep.swap(hs.rep); + } + + iterator begin() const { + return rep.begin(); + } + iterator end() const { + return rep.end(); + } + iterator cbegin() const { + return rep.begin(); + } + iterator cend() const { + return rep.end(); + } + +public: + iterator insert(const value_type& obj) { + return rep.insert_equal(obj); + } + template + iterator emplace(Args&&... args) { + return rep.emplace_equal(std::forward(args)...); + } + template + void insert(InputIterator f, InputIterator l) { + rep.insert_equal(f, l); + } + iterator insert_noresize(const value_type& obj) { + return rep.insert_equal_noresize(obj); + } + + template + iterator find(const TKey& key) const { + return rep.find(key); + } + + template + size_type count(const TKey& key) const { + return rep.count(key); + } + + template + std::pair equal_range(const TKey& key) const { + return rep.equal_range(key); + } + + size_type erase(const key_type& key) { + return rep.erase(key); + } + void erase(iterator it) { + rep.erase(it); + } + void erase(iterator f, iterator l) { + rep.erase(f, l); + } + Y_REINITIALIZES_OBJECT void clear() { + rep.clear(); + } + Y_REINITIALIZES_OBJECT void clear(size_t downsize_hint) { + rep.clear(downsize_hint); + } + Y_REINITIALIZES_OBJECT void basic_clear() { + rep.basic_clear(); + } + void release_nodes() { + rep.release_nodes(); + } + +public: + void reserve(size_type hint) { + rep.reserve(hint); + } + size_type bucket_count() const { + return rep.bucket_count(); + } + size_type bucket_size(size_type n) const { + return rep.bucket_size(n); + } + node_allocator_type& GetNodeAllocator() { + return rep.GetNodeAllocator(); + } +}; + +template +inline bool operator==(const THashMultiSet& hs1, const THashMultiSet& hs2) { + if (hs1.size() != hs2.size()) { + return false; + } + EqualKey equalKey; + auto it = hs1.begin(); + while (it != hs1.end()) { + const auto& value = *it; + size_t count = 0; + for (; (it != hs1.end()) && (equalKey(*it, value)); ++it, ++count) { + } + if (hs2.count(value) != count) { + return false; + } + } + return true; +} + +template +inline bool operator!=(const THashMultiSet& hs1, const THashMultiSet& hs2) { + return !(hs1 == hs2); +} diff --git a/util/generic/hash_set.pxd b/util/generic/hash_set.pxd new file mode 100644 index 00000000000..d28d90cbe77 --- /dev/null +++ b/util/generic/hash_set.pxd @@ -0,0 +1,43 @@ +from libcpp.pair cimport pair + +cdef extern from "util/generic/hash_set.h" nogil: + cdef cppclass THashSet[T]: + cppclass iterator: + T& operator*() + iterator operator++() + iterator operator--() + bint operator==(iterator) + bint operator!=(iterator) + + cppclass const_iterator(iterator): + pass + + THashSet() except + + THashSet(THashSet&) except + + THashSet(T* t) except + + THashSet& operator=(THashSet&) + + bint operator==(THashSet&) + bint operator!=(THashSet&) + bint operator<(THashSet&) + bint operator>(THashSet&) + bint operator<=(THashSet&) + bint operator>=(THashSet&) + + iterator begin() + const_iterator const_begin "begin"() + void clear() + size_t count(T&) + bint empty() + iterator end() + const_iterator const_end "end"() + void erase(iterator) except + + void erase(iterator, iterator) except + + size_t erase(T&) + iterator find(T&) + bint contains(T&) + const_iterator const_find "find"(T&) + pair[iterator, bint] insert(T) + iterator insert(iterator, T) + size_t size() + void swap(THashSet&) diff --git a/util/generic/hash_set_ut.pyx b/util/generic/hash_set_ut.pyx new file mode 100644 index 00000000000..bdcf6284afa --- /dev/null +++ b/util/generic/hash_set_ut.pyx @@ -0,0 +1,53 @@ +# cython: c_string_type=str, c_string_encoding=utf8 + +from util.generic.hash_set cimport THashSet +from util.generic.string cimport TString + +import pytest +import unittest + +from cython.operator cimport dereference as deref + + +class TestHashSet(unittest.TestCase): + + def test_simple_constructor_equality_operator(self): + cdef THashSet[int] c1 + c1.insert(1) + assert c1.size() == 1 + c1.insert(2) + c1.insert(2) + c1.insert(2) + c1.insert(2) + assert c1.size() == 2 + assert c1.contains(2) + assert not c1.contains(5) + cdef THashSet[int] c2 = c1 + assert c1 == c2 + c1.insert(3) + assert c1 != c2 + c1.erase(3) + assert c1 == c2 + + def test_insert_erase(self): + cdef THashSet[TString] tmp + self.assertTrue(tmp.insert("one").second) + self.assertFalse(tmp.insert("one").second) + self.assertTrue(tmp.insert("two").second) + cdef TString one = "one" + cdef TString two = "two" + self.assertEqual(tmp.erase(one), 1) + self.assertEqual(tmp.erase(two), 1) + self.assertEqual(tmp.size(), 0) + self.assertTrue(tmp.empty()) + + def test_iterators_and_find(self): + cdef THashSet[TString] tmp + self.assertTrue(tmp.begin() == tmp.end()) + self.assertTrue(tmp.find("1") == tmp.end()) + tmp.insert("1") + self.assertTrue(tmp.begin() != tmp.end()) + cdef THashSet[TString].iterator it = tmp.find("1") + self.assertTrue(it != tmp.end()) + self.assertEqual(deref(it), "1") + diff --git a/util/generic/hash_table.cpp b/util/generic/hash_table.cpp new file mode 100644 index 00000000000..a6b119e821e --- /dev/null +++ b/util/generic/hash_table.cpp @@ -0,0 +1,51 @@ +#include "hash_table.h" + +#include +#include + +const void* const _yhashtable_empty_data[] = {(void*)3, nullptr, (void*)1}; + +TString NPrivate::MapKeyToString(TStringBuf key) { + constexpr size_t HASH_KEY_MAX_LENGTH = 500; + try { + return EscapeC(key.substr(0, HASH_KEY_MAX_LENGTH)); + } catch (...) { + return "TStringBuf"; + } +} + +TString NPrivate::MapKeyToString(unsigned short key) { + return ToString(key); +} + +TString NPrivate::MapKeyToString(short key) { + return ToString(key); +} + +TString NPrivate::MapKeyToString(unsigned int key) { + return ToString(key); +} + +TString NPrivate::MapKeyToString(int key) { + return ToString(key); +} + +TString NPrivate::MapKeyToString(unsigned long key) { + return ToString(key); +} + +TString NPrivate::MapKeyToString(long key) { + return ToString(key); +} + +TString NPrivate::MapKeyToString(unsigned long long key) { + return ToString(key); +} + +TString NPrivate::MapKeyToString(long long key) { + return ToString(key); +} + +void NPrivate::ThrowKeyNotFoundInHashTableException(const TStringBuf keyRepresentation) { + ythrow yexception() << "Key not found in hashtable: " << keyRepresentation; +} diff --git a/util/generic/hash_table.h b/util/generic/hash_table.h new file mode 100644 index 00000000000..0fbbef67892 --- /dev/null +++ b/util/generic/hash_table.h @@ -0,0 +1,1440 @@ +#pragma once + +#include "fwd.h" +#include "mapfindptr.h" + +#include +#include +#include +#include +#include +#include "yexception.h" +#include "typetraits.h" +#include "utility.h" + +#include +#include +#include +#include +#include + +#include + +#include "hash_primes.h" + +struct TSelect1st { + template + inline const typename TPair::first_type& operator()(const TPair& x) const { + return x.first; + } +}; + +template +struct __yhashtable_node { + /** If the first bit is not set, then this is a pointer to the next node in + * the list of nodes for the current bucket. Otherwise this is a pointer of + * type __yhashtable_node**, pointing back into the buckets array. + * + * This trick makes it possible to use only one node pointer in a hash table + * iterator. */ + __yhashtable_node* next; + + /** Value stored in a node. */ + Value val; + + __yhashtable_node& operator=(const __yhashtable_node&) = delete; +}; + +template +class THashTable; + +template +class sthash; + +template +struct __yhashtable_iterator; + +template +struct __yhashtable_const_iterator; + +template +struct __yhashtable_iterator { + using iterator = __yhashtable_iterator; + using const_iterator = __yhashtable_const_iterator; + using node = __yhashtable_node; + + using iterator_category = std::forward_iterator_tag; + using value_type = Value; + using difference_type = ptrdiff_t; + using size_type = size_t; + using reference = Value&; + using pointer = Value*; + + node* cur; + + explicit __yhashtable_iterator(node* n) + : cur(n) + { + } /*y*/ + __yhashtable_iterator() = default; + + reference operator*() const { + return cur->val; + } + pointer operator->() const { + return &(operator*()); + } + iterator& operator++(); + iterator operator++(int); + friend bool operator==(const iterator& l, const iterator& r) { + return l.cur == r.cur; + } + friend bool operator!=(const iterator& l, const iterator& r) { + return l.cur != r.cur; + } + bool IsEnd() const { + return !cur; + } + Y_FORCE_INLINE explicit operator bool() const noexcept { + return cur != nullptr; + } +}; + +template +struct __yhashtable_const_iterator { + using iterator = __yhashtable_iterator; + using const_iterator = __yhashtable_const_iterator; + using node = __yhashtable_node; + + using iterator_category = std::forward_iterator_tag; + using value_type = Value; + using difference_type = ptrdiff_t; + using size_type = size_t; + using reference = const Value&; + using pointer = const Value*; + + const node* cur; + + explicit __yhashtable_const_iterator(const node* n) + : cur(n) + { + } + __yhashtable_const_iterator() { + } + __yhashtable_const_iterator(const iterator& it) + : cur(it.cur) + { + } + reference operator*() const { + return cur->val; + } + pointer operator->() const { + return &(operator*()); + } + const_iterator& operator++(); + const_iterator operator++(int); + friend bool operator==(const const_iterator& l, const const_iterator& r) { + return l.cur == r.cur; + } + friend bool operator!=(const const_iterator& l, const const_iterator& r) { + return l.cur != r.cur; + } + bool IsEnd() const { + return !cur; + } + Y_FORCE_INLINE explicit operator bool() const noexcept { + return cur != nullptr; + } +}; + +/** + * This class saves some space in allocator-based containers for the most common + * use case of empty allocators. This is achieved thanks to the application of + * empty base class optimization (aka EBCO). + */ +template +class _allocator_base: private Alloc { +public: + _allocator_base(const Alloc& other) + : Alloc(other) + { + } + + Alloc& _get_alloc() { + return static_cast(*this); + } + const Alloc& _get_alloc() const { + return static_cast(*this); + } + void _set_alloc(const Alloc& allocator) { + _get_alloc() = allocator; + } + + void swap(_allocator_base& other) { + DoSwap(_get_alloc(), other._get_alloc()); + } +}; + +/** + * Wrapper for an array of THashTable buckets. + * + * Is better than vector for this particular use case. Main differences: + * - Occupies one less word on stack. + * - Doesn't even try to initialize its elements. It is THashTable's responsibility. + * - Presents a better interface in relation to THashTable's marker element trick. + * + * Internally this class is just a pointer-size pair, and the data on the heap + * has the following structure: + * + * +----------+----------------------+----------+-------------------------+ + * | raw_size | elements ... | marker | unused space [optional] | + * +----------+----------------------+----------+-------------------------+ + * ^ ^ + * | | + * Data points here end() points here + * + * `raw_size` stores the size of the allocated memory block. It is used to + * support resizing without reallocation. + * + * `marker` is a special marker element that is set by the THashTable that is + * then used in iterator implementation to know when the end is reached. + * + * Unused space at the end of the memory block may not be present. + */ +template +class _yhashtable_buckets: private _allocator_base { + using base_type = _allocator_base; + + static_assert(sizeof(T) == sizeof(size_t), "T is expected to be the same size as size_t."); + +public: + using allocator_type = Alloc; + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using iterator = pointer; + using const_iterator = const_pointer; + using size_type = size_t; + using difference_type = ptrdiff_t; + using TBucketDivisor = ::NPrivate::THashDivisor; + + _yhashtable_buckets(const Alloc& other) + : base_type(other) + , Data(nullptr) + , Size() + { + } + + ~_yhashtable_buckets() { + Y_ASSERT(!Data); + } + + void initialize_dynamic(TBucketDivisor size) { + Y_ASSERT(!Data); + + Data = this->_get_alloc().allocate(size() + 2) + 1; + Size = size; + + *reinterpret_cast(Data - 1) = size() + 2; + } + + void deinitialize_dynamic() { + Y_ASSERT(Data); + + this->_get_alloc().deallocate(Data - 1, *reinterpret_cast(Data - 1)); + Data = pointer(); + Size = TBucketDivisor(); + } + + void initialize_static(pointer data, TBucketDivisor size) { + Y_ASSERT(!Data && data && size() >= 1); + + Data = data; + Size = size; + } + + void deinitialize_static() { + Y_ASSERT(Data); + + Data = pointer(); + Size = TBucketDivisor(); + } + + void resize_noallocate(TBucketDivisor size) { + Y_ASSERT(size() <= capacity()); + + Size = size; + } + + iterator begin() { + return Data; + } + const_iterator begin() const { + return Data; + } + iterator end() { + return Data + Size(); + } + const_iterator end() const { + return Data + Size(); + } + + pointer data() { + return Data; + } + const_pointer data() const { + return Data; + } + + size_type size() const { + return Size(); + } + size_type capacity() const { + return *reinterpret_cast(Data - 1); + } + TBucketDivisor ExtSize() const { + return Size; + } + int BucketDivisorHint() const { + return +Size.Hint; + } + + allocator_type get_allocator() const { + return this->_get_alloc(); + } + + const_reference operator[](size_type index) const { + Y_ASSERT(index <= Size()); + + return *(Data + index); + } + + reference operator[](size_type index) { + Y_ASSERT(index <= Size()); + + return *(Data + index); + } + + void swap(_yhashtable_buckets& other) { + base_type::swap(other); + DoSwap(Data, other.Data); + DoSwap(Size, other.Size); + } + +private: + /** Pointer to the first element of the buckets array. */ + pointer Data; + + /** Size of the buckets array. Doesn't take the marker element at the end into account. */ + TBucketDivisor Size; +}; + +/** + * This class saves one word in THashTable for the most common use case of empty + * functors. The exact implementation picks a specialization with storage allocated + * for the functors if those are non-empty, and another specialization that creates + * functors on the fly if they are empty. It is expected that empty functors have + * trivial constructors. + * + * Note that this is basically the only way to do it portably. Another option is + * multiple inheritance from empty functors, but MSVC's empty base class + * optimization chokes up on multiple empty bases, and we're already using + * EBCO in _allocator_base. + * + * Note that there are no specializations for the case when only one or two + * of the functors are empty as this is a case that's just way too rare. + */ +template ::value&& std::is_empty::value&& std::is_empty::value> +class _yhashtable_base: public _allocator_base { + using base_type = _allocator_base; + +public: + _yhashtable_base(const HashFcn& hash, const ExtractKey& extract, const EqualKey& equals, const Alloc& alloc) + : base_type(alloc) + , hash_(hash) + , extract_(extract) + , equals_(equals) + { + } + + const EqualKey& _get_key_eq() const { + return equals_; + } + EqualKey& _get_key_eq() { + return equals_; + } + void _set_key_eq(const EqualKey& equals) { + this->equals_ = equals; + } + + const ExtractKey& _get_key_extract() const { + return extract_; + } + ExtractKey& _get_key_extract() { + return extract_; + } + void _set_key_extract(const ExtractKey& extract) { + this->extract_ = extract; + } + + const HashFcn& _get_hash_fun() const { + return hash_; + } + HashFcn& _get_hash_fun() { + return hash_; + } + void _set_hash_fun(const HashFcn& hash) { + this->hash_ = hash; + } + + void swap(_yhashtable_base& other) { + base_type::swap(other); + DoSwap(equals_, other.equals_); + DoSwap(extract_, other.extract_); + DoSwap(hash_, other.hash_); + } + +private: + HashFcn hash_; + ExtractKey extract_; + EqualKey equals_; +}; + +template +class _yhashtable_base: public _allocator_base { + using base_type = _allocator_base; + +public: + _yhashtable_base(const HashFcn&, const ExtractKey&, const EqualKey&, const Alloc& alloc) + : base_type(alloc) + { + } + + EqualKey _get_key_eq() const { + return EqualKey(); + } + void _set_key_eq(const EqualKey&) { + } + + ExtractKey _get_key_extract() const { + return ExtractKey(); + } + void _set_key_extract(const ExtractKey&) { + } + + HashFcn _get_hash_fun() const { + return HashFcn(); + } + void _set_hash_fun(const HashFcn&) { + } + + void swap(_yhashtable_base& other) { + base_type::swap(other); + } +}; + +template +struct _yhashtable_traits { + using node = __yhashtable_node; + + using node_allocator_type = TReboundAllocator; + using nodep_allocator_type = TReboundAllocator; + + using base_type = _yhashtable_base; +}; + +extern const void* const _yhashtable_empty_data[]; + +template +class THashTable: private _yhashtable_traits::base_type { + using traits_type = _yhashtable_traits; + using base_type = typename traits_type::base_type; + using node = typename traits_type::node; + using nodep_allocator_type = typename traits_type::nodep_allocator_type; + using buckets_type = _yhashtable_buckets; + using TBucketDivisor = ::NPrivate::THashDivisor; + +public: + using key_type = Key; + using value_type = Value; + using hasher = HashFcn; + using key_equal = EqualKey; + using key_extract = ExtractKey; + using allocator_type = Alloc; + using node_allocator_type = typename traits_type::node_allocator_type; + + using size_type = size_t; + using difference_type = ptrdiff_t; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + + node_allocator_type& GetNodeAllocator() { + return this->_get_alloc(); + } + const node_allocator_type& GetNodeAllocator() const { + return this->_get_alloc(); + } + key_equal key_eq() const { + return this->_get_key_eq(); + } + hasher hash_function() const { + return this->_get_hash_fun(); + } + +private: + template + bool equals(const KeyL& l, const KeyR& r) const { + return this->_get_key_eq()(l, r); + } + + /* This method is templated to postpone instantiation of key extraction functor. */ + template + auto get_key(const ValueL& value) const -> decltype(ExtractKey()(value)) { + return this->_get_key_extract()(value); + } + + node* get_node() { + node* result = this->_get_alloc().allocate(1); + Y_ASSERT((reinterpret_cast(result) & 1) == 0); /* We're using the last bit of the node pointer. */ + return result; + } + void put_node(node* p) { + this->_get_alloc().deallocate(p, 1); + } + + buckets_type buckets; + size_type num_elements; + +public: + using iterator = __yhashtable_iterator; + using const_iterator = __yhashtable_const_iterator; + using insert_ctx = node**; + + friend struct __yhashtable_iterator; + friend struct __yhashtable_const_iterator; + +public: + THashTable() + : base_type(HashFcn(), ExtractKey(), EqualKey(), node_allocator_type()) + , buckets(nodep_allocator_type()) + , num_elements(0) + { + initialize_buckets(buckets, 0); + } + + THashTable(size_type n, const HashFcn& hf, const EqualKey& eql, const ExtractKey& ext) + : base_type(hf, ext, eql, node_allocator_type()) + , buckets(nodep_allocator_type()) + , num_elements(0) + { + initialize_buckets(buckets, n); + } + + THashTable(size_type n, const HashFcn& hf, const EqualKey& eql) + : base_type(hf, ExtractKey(), eql, node_allocator_type()) + , buckets(nodep_allocator_type()) + , num_elements(0) + { + initialize_buckets(buckets, n); + } + + template + THashTable(size_type n, const HashFcn& hf, const EqualKey& eql, TAllocParam* allocParam) + : base_type(hf, ExtractKey(), eql, allocParam) + , buckets(allocParam) + , num_elements(0) + { + initialize_buckets(buckets, n); + } + + THashTable(const THashTable& ht) + : base_type(ht._get_hash_fun(), ht._get_key_extract(), ht._get_key_eq(), ht._get_alloc()) + , buckets(ht.buckets.get_allocator()) + , num_elements(0) + { + if (ht.empty()) { + initialize_buckets(buckets, 0); + } else { + initialize_buckets_dynamic(buckets, ht.buckets.ExtSize()); + copy_from_dynamic(ht); + } + } + + THashTable(THashTable&& ht) noexcept + : base_type(ht._get_hash_fun(), ht._get_key_extract(), ht._get_key_eq(), ht._get_alloc()) + , buckets(ht.buckets.get_allocator()) + , num_elements(0) + { + initialize_buckets(buckets, 0); + this->swap(ht); + } + + THashTable& operator=(const THashTable& ht) { + if (&ht != this) { + basic_clear(); + this->_set_hash_fun(ht._get_hash_fun()); + this->_set_key_eq(ht._get_key_eq()); + this->_set_key_extract(ht._get_key_extract()); + /* We don't copy allocator for a reason. */ + + if (ht.empty()) { + /* Some of the old code in Arcadia works around the behavior in + * clear() by invoking operator= with empty hash as an argument. + * It's expected that this will deallocate the buckets array, so + * this is what we have to do here. */ + deinitialize_buckets(buckets); + initialize_buckets(buckets, 0); + } else { + if (buckets.capacity() > ht.buckets.size()) { + buckets.resize_noallocate(ht.buckets.ExtSize()); + } else { + deinitialize_buckets(buckets); + initialize_buckets_dynamic(buckets, ht.buckets.ExtSize()); + } + + copy_from_dynamic(ht); + } + } + return *this; + } + + THashTable& operator=(THashTable&& ht) noexcept { + basic_clear(); + swap(ht); + + return *this; + } + + ~THashTable() { + basic_clear(); + deinitialize_buckets(buckets); + } + + size_type size() const noexcept { + return num_elements; + } + size_type max_size() const noexcept { + return size_type(-1); + } + + Y_PURE_FUNCTION bool empty() const noexcept { + return size() == 0; + } + + void swap(THashTable& ht) { + base_type::swap(ht); + buckets.swap(ht.buckets); + DoSwap(num_elements, ht.num_elements); + } + + iterator begin() { + for (size_type n = 0; n < buckets.size(); ++n) /*y*/ + if (buckets[n]) + return iterator(buckets[n]); /*y*/ + return end(); + } + + iterator end() { + return iterator(nullptr); + } /*y*/ + + const_iterator begin() const { + for (size_type n = 0; n < buckets.size(); ++n) /*y*/ + if (buckets[n]) + return const_iterator(buckets[n]); /*y*/ + return end(); + } + + const_iterator end() const { + return const_iterator(nullptr); + } /*y*/ + +public: + size_type bucket_count() const { + return buckets.size(); + } /*y*/ + + size_type bucket_size(size_type bucket) const { + size_type result = 0; + if (const node* cur = buckets[bucket]) + for (; !((uintptr_t)cur & 1); cur = cur->next) + result += 1; + return result; + } + + template + std::pair insert_unique(const OtherValue& obj) { + reserve(num_elements + 1); + return insert_unique_noresize(obj); + } + + template + iterator insert_equal(const OtherValue& obj) { + reserve(num_elements + 1); + return emplace_equal_noresize(obj); + } + + template + iterator emplace_equal(Args&&... args) { + reserve(num_elements + 1); + return emplace_equal_noresize(std::forward(args)...); + } + + template + iterator insert_direct(const OtherValue& obj, insert_ctx ins) { + return emplace_direct(ins, obj); + } + + template + iterator emplace_direct(insert_ctx ins, Args&&... args) { + bool resized = reserve(num_elements + 1); + node* tmp = new_node(std::forward(args)...); + if (resized) { + find_i(get_key(tmp->val), ins); + } + tmp->next = *ins ? *ins : (node*)((uintptr_t)(ins + 1) | 1); + *ins = tmp; + ++num_elements; + return iterator(tmp); + } + + template + std::pair emplace_unique(Args&&... args) { + reserve(num_elements + 1); + return emplace_unique_noresize(std::forward(args)...); + } + + template + std::pair emplace_unique_noresize(Args&&... args); + + template + std::pair insert_unique_noresize(const OtherValue& obj); + + template + iterator emplace_equal_noresize(Args&&... args); + + template + void insert_unique(InputIterator f, InputIterator l) { + insert_unique(f, l, typename std::iterator_traits::iterator_category()); + } + + template + void insert_equal(InputIterator f, InputIterator l) { + insert_equal(f, l, typename std::iterator_traits::iterator_category()); + } + + template + void insert_unique(InputIterator f, InputIterator l, std::input_iterator_tag) { + for (; f != l; ++f) + insert_unique(*f); + } + + template + void insert_equal(InputIterator f, InputIterator l, std::input_iterator_tag) { + for (; f != l; ++f) + insert_equal(*f); + } + + template + void insert_unique(ForwardIterator f, ForwardIterator l, std::forward_iterator_tag) { + difference_type n = std::distance(f, l); + + reserve(num_elements + n); + for (; n > 0; --n, ++f) + insert_unique_noresize(*f); + } + + template + void insert_equal(ForwardIterator f, ForwardIterator l, std::forward_iterator_tag) { + difference_type n = std::distance(f, l); + + reserve(num_elements + n); + for (; n > 0; --n, ++f) + emplace_equal_noresize(*f); + } + + template + reference find_or_insert(const OtherValue& v); + + template + iterator find(const OtherKey& key) { + size_type n = bkt_num_key(key); + node* first; + for (first = buckets[n]; + first && !equals(get_key(first->val), key); + first = ((uintptr_t)first->next & 1) ? nullptr : first->next) /*y*/ + { + } + return iterator(first); /*y*/ + } + + template + const_iterator find(const OtherKey& key) const { + size_type n = bkt_num_key(key); + const node* first; + for (first = buckets[n]; + first && !equals(get_key(first->val), key); + first = ((uintptr_t)first->next & 1) ? nullptr : first->next) /*y*/ + { + } + return const_iterator(first); /*y*/ + } + + template + iterator find_i(const OtherKey& key, insert_ctx& ins); + + template + size_type count(const OtherKey& key) const { + const size_type n = bkt_num_key(key); + size_type result = 0; + + if (const node* cur = buckets[n]) + for (; !((uintptr_t)cur & 1); cur = cur->next) + if (equals(get_key(cur->val), key)) + ++result; + return result; + } + + template + std::pair equal_range(const OtherKey& key); + + template + std::pair equal_range(const OtherKey& key) const; + + template + std::pair equal_range_i(const OtherKey& key, insert_ctx& ins); + + template + size_type erase(const OtherKey& key); + + template + size_type erase_one(const OtherKey& key); + + // void (instead of iterator) is intended, see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2023.pdf + void erase(const iterator& it); + void erase(iterator first, iterator last); + + void erase(const const_iterator& it); + void erase(const_iterator first, const_iterator last); + + bool reserve(size_type num_elements_hint); + Y_REINITIALIZES_OBJECT void basic_clear(); + + /** + * Clears the hashtable without deallocating the nodes. + * + * This might come in handy with non-standard allocators, e.g. a pool + * allocator with a pool that is then cleared manually, thus releasing all + * the nodes at once. + */ + void release_nodes() { + if (empty()) + return; /* Need this check because empty buckets may reside in read-only memory. */ + + clear_buckets(buckets); + num_elements = 0; + } + + // implemented in save_stl.h + template + int save_for_st(IOutputStream* stream, KeySaver& ks, sthash, TEqualTo, typename KeySaver::TSizeType>* stHash = nullptr) const; + + Y_REINITIALIZES_OBJECT void clear(size_type downsize) { + basic_clear(); + + if (downsize < buckets.size()) { + const TBucketDivisor newSize = HashBucketCountExt(downsize); + if (newSize() < buckets.size()) { + Y_ASSERT(newSize() >= 7); /* We cannot downsize static buckets. */ + buckets.resize_noallocate(newSize); + } + } + } + + /** + * Clears the hashtable and tries to reasonably downsize it. Note that + * downsizing is mainly for the following use case: + * + * THashTable hash; + * for(...) { + * if (someCond()) + * hash.clear(); + * hash.insert(...); + * } + * + * Here if at some point `hash` gets really big, then all the following calls + * to `clear` become really slow as they have to iterate through all the the + * empty buckets. This is worked around by squeezing the buckets array a little + * bit with every `clear` call. + * + * Alternatively, the user can call `basic_clear`, which doesn't do the + * downsizing. + */ + Y_REINITIALIZES_OBJECT void clear() { + if (num_elements) + clear((num_elements * 2 + buckets.size()) / 3); + } + +private: + static void initialize_buckets(buckets_type& buckets, size_type sizeHint) { + if (sizeHint == 0) { + buckets.initialize_static(reinterpret_cast(const_cast(_yhashtable_empty_data)) + 1, TBucketDivisor::One()); + } else { + TBucketDivisor size = HashBucketCountExt(sizeHint); + Y_ASSERT(size() >= 7); + + initialize_buckets_dynamic(buckets, size); + } + } + + static void initialize_buckets_dynamic(buckets_type& buckets, TBucketDivisor size) { + buckets.initialize_dynamic(size); + memset(buckets.data(), 0, size() * sizeof(*buckets.data())); + buckets[size()] = (node*)1; + } + + static void deinitialize_buckets(buckets_type& buckets) { + if (buckets.size() == 1) { + buckets.deinitialize_static(); + } else { + buckets.deinitialize_dynamic(); + } + } + + static void clear_buckets(buckets_type& buckets) { + memset(buckets.data(), 0, buckets.size() * sizeof(*buckets.data())); + } + + template + size_type bkt_num_key(const OtherKey& key) const { + return bkt_num_key(key, buckets.ExtSize()); + } + + template + size_type bkt_num(const OtherValue& obj) const { + return bkt_num_key(get_key(obj)); + } + + template + size_type bkt_num_key(const OtherKey& key, TBucketDivisor n) const { + const size_type bucket = n.Remainder(this->_get_hash_fun()(key)); + Y_ASSERT((0 <= bucket) && (bucket < n())); + return bucket; + } + + template + size_type bkt_num(const OtherValue& obj, TBucketDivisor n) const { + return bkt_num_key(get_key(obj), n); + } + + template + node* new_node(Args&&... val) { + node* n = get_node(); + n->next = (node*)1; /*y*/ // just for a case + try { + new (static_cast(&n->val)) Value(std::forward(val)...); + } catch (...) { + put_node(n); + throw; + } + return n; + } + + void delete_node(node* n) { + n->val.~Value(); + // n->next = (node*) 0xDeadBeeful; + put_node(n); + } + + void erase_bucket(const size_type n, node* first, node* last); + void erase_bucket(const size_type n, node* last); + + void copy_from_dynamic(const THashTable& ht); +}; + +template +__yhashtable_iterator& __yhashtable_iterator::operator++() { + Y_ASSERT(cur); + cur = cur->next; + if ((uintptr_t)cur & 1) { + node** bucket = (node**)((uintptr_t)cur & ~1); + while (*bucket == nullptr) + ++bucket; + Y_ASSERT(*bucket != nullptr); + cur = (node*)((uintptr_t)*bucket & ~1); + } + return *this; +} + +template +inline __yhashtable_iterator __yhashtable_iterator::operator++(int) { + iterator tmp = *this; + ++*this; + return tmp; +} + +template +__yhashtable_const_iterator& __yhashtable_const_iterator::operator++() { + Y_ASSERT(cur); + cur = cur->next; + if ((uintptr_t)cur & 1) { + node** bucket = (node**)((uintptr_t)cur & ~1); + while (*bucket == nullptr) + ++bucket; + Y_ASSERT(*bucket != nullptr); + cur = (node*)((uintptr_t)*bucket & ~1); + } + return *this; +} + +template +inline __yhashtable_const_iterator __yhashtable_const_iterator::operator++(int) { + const_iterator tmp = *this; + ++*this; + return tmp; +} + +template +template +std::pair::iterator, bool> THashTable::emplace_unique_noresize(Args&&... args) { + auto deleter = [&](node* tmp) { delete_node(tmp); }; + node* tmp = new_node(std::forward(args)...); + std::unique_ptr guard(tmp, deleter); + + const size_type n = bkt_num(tmp->val); + node* first = buckets[n]; + + if (first) /*y*/ + for (node* cur = first; !((uintptr_t)cur & 1); cur = cur->next) /*y*/ + if (equals(get_key(cur->val), get_key(tmp->val))) + return std::pair(iterator(cur), false); /*y*/ + + guard.release(); + tmp->next = first ? first : (node*)((uintptr_t)&buckets[n + 1] | 1); /*y*/ + buckets[n] = tmp; + ++num_elements; + return std::pair(iterator(tmp), true); /*y*/ +} + +template +template +std::pair::iterator, bool> THashTable::insert_unique_noresize(const OtherValue& obj) { + const size_type n = bkt_num(obj); + node* first = buckets[n]; + + if (first) /*y*/ + for (node* cur = first; !((uintptr_t)cur & 1); cur = cur->next) /*y*/ + if (equals(get_key(cur->val), get_key(obj))) + return std::pair(iterator(cur), false); /*y*/ + + node* tmp = new_node(obj); + tmp->next = first ? first : (node*)((uintptr_t)&buckets[n + 1] | 1); /*y*/ + buckets[n] = tmp; + ++num_elements; + return std::pair(iterator(tmp), true); /*y*/ +} + +template +template +__yhashtable_iterator THashTable::emplace_equal_noresize(Args&&... args) { + auto deleter = [&](node* tmp) { delete_node(tmp); }; + node* tmp = new_node(std::forward(args)...); + std::unique_ptr guard(tmp, deleter); + const size_type n = bkt_num(tmp->val); + node* first = buckets[n]; + + if (first) /*y*/ + for (node* cur = first; !((uintptr_t)cur & 1); cur = cur->next) /*y*/ + if (equals(get_key(cur->val), get_key(tmp->val))) { + guard.release(); + tmp->next = cur->next; + cur->next = tmp; + ++num_elements; + return iterator(tmp); /*y*/ + } + + guard.release(); + tmp->next = first ? first : (node*)((uintptr_t)&buckets[n + 1] | 1); /*y*/ + buckets[n] = tmp; + ++num_elements; + return iterator(tmp); /*y*/ +} + +template +template +typename THashTable::reference THashTable::find_or_insert(const OtherValue& v) { + reserve(num_elements + 1); + + size_type n = bkt_num_key(get_key(v)); + node* first = buckets[n]; + + if (first) /*y*/ + for (node* cur = first; !((uintptr_t)cur & 1); cur = cur->next) /*y*/ + if (equals(get_key(cur->val), get_key(v))) + return cur->val; + + node* tmp = new_node(v); + tmp->next = first ? first : (node*)((uintptr_t)&buckets[n + 1] | 1); /*y*/ + buckets[n] = tmp; + ++num_elements; + return tmp->val; +} + +template +template +__yhashtable_iterator THashTable::find_i(const OtherKey& key, insert_ctx& ins) { + size_type n = bkt_num_key(key); + ins = &buckets[n]; + node* first = buckets[n]; + + if (first) /*y*/ + for (node* cur = first; !((uintptr_t)cur & 1); cur = cur->next) /*y*/ + if (equals(get_key(cur->val), key)) + return iterator(cur); /*y*/ + return end(); +} + +template +template +std::pair<__yhashtable_iterator, __yhashtable_iterator> THashTable::equal_range(const OtherKey& key) { + insert_ctx ctx; + return equal_range_i(key, ctx); +} + +template +template +std::pair<__yhashtable_iterator, __yhashtable_iterator> THashTable::equal_range_i(const OtherKey& key, insert_ctx& ins) { + using pii = std::pair; + const size_type n = bkt_num_key(key); + ins = &buckets[n]; + node* first = buckets[n]; + + if (first) /*y*/ + for (; !((uintptr_t)first & 1); first = first->next) { /*y*/ + if (equals(get_key(first->val), key)) { + for (node* cur = first->next; !((uintptr_t)cur & 1); cur = cur->next) + if (!equals(get_key(cur->val), key)) + return pii(iterator(first), iterator(cur)); /*y*/ + for (size_type m = n + 1; m < buckets.size(); ++m) /*y*/ + if (buckets[m]) + return pii(iterator(first), /*y*/ + iterator(buckets[m])); /*y*/ + return pii(iterator(first), end()); /*y*/ + } + } + return pii(end(), end()); +} + +template +template +std::pair<__yhashtable_const_iterator, __yhashtable_const_iterator> THashTable::equal_range(const OtherKey& key) const { + using pii = std::pair; + const size_type n = bkt_num_key(key); + const node* first = buckets[n]; + + if (first) /*y*/ + for (; !((uintptr_t)first & 1); first = first->next) { /*y*/ + if (equals(get_key(first->val), key)) { + for (const node* cur = first->next; !((uintptr_t)cur & 1); cur = cur->next) + if (!equals(get_key(cur->val), key)) + return pii(const_iterator(first), /*y*/ + const_iterator(cur)); /*y*/ + for (size_type m = n + 1; m < buckets.size(); ++m) /*y*/ + if (buckets[m]) + return pii(const_iterator(first /*y*/), + const_iterator(buckets[m] /*y*/)); + return pii(const_iterator(first /*y*/), end()); + } + } + return pii(end(), end()); +} + +template +template +typename THashTable::size_type THashTable::erase(const OtherKey& key) { + const size_type n = bkt_num_key(key); + node* first = buckets[n]; + size_type erased = 0; + + if (first) { + node* cur = first; + node* next = cur->next; + while (!((uintptr_t)next & 1)) { /*y*/ + if (equals(get_key(next->val), key)) { + cur->next = next->next; + ++erased; + --num_elements; + delete_node(next); + next = cur->next; + } else { + cur = next; + next = cur->next; + } + } + if (equals(get_key(first->val), key)) { + buckets[n] = ((uintptr_t)first->next & 1) ? nullptr : first->next; /*y*/ + ++erased; + --num_elements; + delete_node(first); + } + } + return erased; +} + +template +template +typename THashTable::size_type THashTable::erase_one(const OtherKey& key) { + const size_type n = bkt_num_key(key); + node* first = buckets[n]; + + if (first) { + node* cur = first; + node* next = cur->next; + while (!((uintptr_t)next & 1)) { /*y*/ + if (equals(get_key(next->val), key)) { + cur->next = next->next; + --num_elements; + delete_node(next); + return 1; + } else { + cur = next; + next = cur->next; + } + } + if (equals(get_key(first->val), key)) { + buckets[n] = ((uintptr_t)first->next & 1) ? nullptr : first->next; /*y*/ + --num_elements; + delete_node(first); + return 1; + } + } + return 0; +} + +template +void THashTable::erase(const iterator& it) { + if (node* const p = it.cur) { + const size_type n = bkt_num(p->val); + node* cur = buckets[n]; + + if (cur == p) { + buckets[n] = ((uintptr_t)cur->next & 1) ? nullptr : cur->next; /*y*/ + delete_node(cur); + --num_elements; + } else { + node* next = cur->next; + while (!((uintptr_t)next & 1)) { + if (next == p) { + cur->next = next->next; + delete_node(next); + --num_elements; + break; + } else { + cur = next; + next = cur->next; + } + } + } + } +} + +template +void THashTable::erase(iterator first, iterator last) { + size_type f_bucket = first.cur ? bkt_num(first.cur->val) : buckets.size(); /*y*/ + size_type l_bucket = last.cur ? bkt_num(last.cur->val) : buckets.size(); /*y*/ + + if (first.cur == last.cur) + return; + else if (f_bucket == l_bucket) + erase_bucket(f_bucket, first.cur, last.cur); + else { + erase_bucket(f_bucket, first.cur, nullptr); + for (size_type n = f_bucket + 1; n < l_bucket; ++n) + erase_bucket(n, nullptr); + if (l_bucket != buckets.size()) /*y*/ + erase_bucket(l_bucket, last.cur); + } +} + +template +inline void +THashTable::erase(const_iterator first, const_iterator last) { + erase(iterator(const_cast(first.cur)), iterator(const_cast(last.cur))); +} + +template +inline void THashTable::erase(const const_iterator& it) { + erase(iterator(const_cast(it.cur))); +} + +template +bool THashTable::reserve(size_type num_elements_hint) { + const size_type old_n = buckets.size(); /*y*/ + if (num_elements_hint + 1 > old_n) { + if (old_n != 1 && num_elements_hint <= old_n) // TODO: this if is for backwards compatibility down to order-in-buckets level. Can be safely removed. + return false; + + const TBucketDivisor n = HashBucketCountExt(num_elements_hint + 1, buckets.BucketDivisorHint() + 1); + if (n() > old_n) { + buckets_type tmp(buckets.get_allocator()); + initialize_buckets_dynamic(tmp, n); +#ifdef __STL_USE_EXCEPTIONS + try { +#endif /* __STL_USE_EXCEPTIONS */ + for (size_type bucket = 0; bucket < old_n; ++bucket) { + node* first = buckets[bucket]; + while (first) { + size_type new_bucket = bkt_num(first->val, n); + node* next = first->next; + buckets[bucket] = ((uintptr_t)next & 1) ? nullptr : next; /*y*/ + next = tmp[new_bucket]; + first->next = next ? next : (node*)((uintptr_t) & (tmp[new_bucket + 1]) | 1); /*y*/ + tmp[new_bucket] = first; + first = buckets[bucket]; + } + } + + buckets.swap(tmp); + deinitialize_buckets(tmp); + + return true; +#ifdef __STL_USE_EXCEPTIONS + } catch (...) { + for (size_type bucket = 0; bucket < tmp.size() - 1; ++bucket) { + while (tmp[bucket]) { + node* next = tmp[bucket]->next; + delete_node(tmp[bucket]); + tmp[bucket] = ((uintptr_t)next & 1) ? nullptr : next /*y*/; + } + } + throw; + } +#endif /* __STL_USE_EXCEPTIONS */ + } + } + + return false; +} + +template +void THashTable::erase_bucket(const size_type n, node* first, node* last) { + node* cur = buckets[n]; + if (cur == first) + erase_bucket(n, last); + else { + node* next; + for (next = cur->next; next != first; cur = next, next = cur->next) + ; + while (next != last) { /*y; 3.1*/ + cur->next = next->next; + delete_node(next); + next = ((uintptr_t)cur->next & 1) ? nullptr : cur->next; /*y*/ + --num_elements; + } + } +} + +template +void THashTable::erase_bucket(const size_type n, node* last) { + node* cur = buckets[n]; + while (cur != last) { + node* next = cur->next; + delete_node(cur); + cur = ((uintptr_t)next & 1) ? nullptr : next; /*y*/ + buckets[n] = cur; + --num_elements; + } +} + +template +void THashTable::basic_clear() { + if (!num_elements) { + return; + } + + node** first = buckets.begin(); + node** last = buckets.end(); + for (; first < last; ++first) { + node* cur = *first; + if (cur) { /*y*/ + while (!((uintptr_t)cur & 1)) { /*y*/ + node* next = cur->next; + delete_node(cur); + cur = next; + } + *first = nullptr; + } + } + num_elements = 0; +} + +template +void THashTable::copy_from_dynamic(const THashTable& ht) { + Y_ASSERT(buckets.size() == ht.buckets.size() && !ht.empty()); + +#ifdef __STL_USE_EXCEPTIONS + try { +#endif /* __STL_USE_EXCEPTIONS */ + for (size_type i = 0; i < ht.buckets.size(); ++i) { /*y*/ + if (const node* cur = ht.buckets[i]) { + node* copy = new_node(cur->val); + buckets[i] = copy; + + for (node* next = cur->next; !((uintptr_t)next & 1); cur = next, next = cur->next) { + copy->next = new_node(next->val); + copy = copy->next; + } + copy->next = (node*)((uintptr_t)&buckets[i + 1] | 1); /*y*/ + } + } + num_elements = ht.num_elements; +#ifdef __STL_USE_EXCEPTIONS + } catch (...) { + basic_clear(); + throw; + } +#endif /* __STL_USE_EXCEPTIONS */ +} + +namespace NPrivate { + template + inline TString MapKeyToString(const Key&) { + return TypeName(); + } + + TString MapKeyToString(TStringBuf key); + TString MapKeyToString(unsigned short key); + TString MapKeyToString(short key); + TString MapKeyToString(unsigned int key); + TString MapKeyToString(int key); + TString MapKeyToString(unsigned long key); + TString MapKeyToString(long key); + TString MapKeyToString(unsigned long long key); + TString MapKeyToString(long long key); + + inline TString MapKeyToString(const TString& key) { + return MapKeyToString(TStringBuf(key)); + } + + inline TString MapKeyToString(const char* key) { + return MapKeyToString(TStringBuf(key)); + } + + inline TString MapKeyToString(char* key) { + return MapKeyToString(TStringBuf(key)); + } + + [[noreturn]] void ThrowKeyNotFoundInHashTableException(const TStringBuf keyRepresentation); +} + +// Cannot name it just 'Hash' because it clashes with too many class members in the code. +template +size_t ComputeHash(const T& value) { + return THash{}(value); +} diff --git a/util/generic/hash_ut.cpp b/util/generic/hash_ut.cpp new file mode 100644 index 00000000000..78fc5657ce9 --- /dev/null +++ b/util/generic/hash_ut.cpp @@ -0,0 +1,1359 @@ +#include "hash.h" +#include "hash_multi_map.h" +#include "vector.h" +#include "hash_set.h" + +#include +#include + +#include +#include +#include + +static const char star = 42; + +class THashTest: public TTestBase { + UNIT_TEST_SUITE(THashTest); + UNIT_TEST(TestHMapConstructorsAndAssignments); + UNIT_TEST(TestHMap1); + UNIT_TEST(TestHMapEqualityOperator); + UNIT_TEST(TestHMMapEqualityOperator); + UNIT_TEST(TestHMMapConstructorsAndAssignments); + UNIT_TEST(TestHMMap1); + UNIT_TEST(TestHMMapHas); + UNIT_TEST(TestHSetConstructorsAndAssignments); + UNIT_TEST(TestHSetSize); + UNIT_TEST(TestHSet2); + UNIT_TEST(TestHSetEqualityOperator); + UNIT_TEST(TestHMSetConstructorsAndAssignments); + UNIT_TEST(TestHMSetSize); + UNIT_TEST(TestHMSet1); + UNIT_TEST(TestHMSetEqualityOperator); + UNIT_TEST(TestHMSetEmplace); + UNIT_TEST(TestInsertErase); + UNIT_TEST(TestResizeOnInsertSmartPtrBug) + UNIT_TEST(TestEmpty); + UNIT_TEST(TestDefaultConstructor); + UNIT_TEST(TestSizeOf); + UNIT_TEST(TestInvariants); + UNIT_TEST(TestAllocation); + UNIT_TEST(TestInsertCopy); + UNIT_TEST(TestEmplace); + UNIT_TEST(TestEmplaceNoresize); + UNIT_TEST(TestEmplaceDirect); + UNIT_TEST(TestTryEmplace); + UNIT_TEST(TestTryEmplaceCopyKey); + UNIT_TEST(TestInsertOrAssign); + UNIT_TEST(TestHMMapEmplace); + UNIT_TEST(TestHMMapEmplaceNoresize); + UNIT_TEST(TestHMMapEmplaceDirect); + UNIT_TEST(TestHSetEmplace); + UNIT_TEST(TestHSetEmplaceNoresize); + UNIT_TEST(TestHSetEmplaceDirect); + UNIT_TEST(TestNonCopyable); + UNIT_TEST(TestValueInitialization); + UNIT_TEST(TestAssignmentClear); + UNIT_TEST(TestReleaseNodes); + UNIT_TEST(TestAt); + UNIT_TEST(TestHMapInitializerList); + UNIT_TEST(TestHMMapInitializerList); + UNIT_TEST(TestHSetInitializerList); + UNIT_TEST(TestHMSetInitializerList); + UNIT_TEST(TestHSetInsertInitializerList); + UNIT_TEST(TestTupleHash); + UNIT_TEST(TestStringHash); + UNIT_TEST(TestFloatingPointHash); + UNIT_TEST_SUITE_END(); + + using hmset = THashMultiSet, TEqualTo>; + +protected: + void TestHMapConstructorsAndAssignments(); + void TestHMap1(); + void TestHMapEqualityOperator(); + void TestHMMapEqualityOperator(); + void TestHMMapConstructorsAndAssignments(); + void TestHMMap1(); + void TestHMMapHas(); + void TestHSetConstructorsAndAssignments(); + void TestHSetSize(); + void TestHSet2(); + void TestHSetEqualityOperator(); + void TestHMSetConstructorsAndAssignments(); + void TestHMSetSize(); + void TestHMSet1(); + void TestHMSetEqualityOperator(); + void TestHMSetEmplace(); + void TestInsertErase(); + void TestResizeOnInsertSmartPtrBug(); + void TestEmpty(); + void TestDefaultConstructor(); + void TestSizeOf(); + void TestInvariants(); + void TestAllocation(); + void TestInsertCopy(); + void TestEmplace(); + void TestEmplaceNoresize(); + void TestEmplaceDirect(); + void TestTryEmplace(); + void TestTryEmplaceCopyKey(); + void TestInsertOrAssign(); + void TestHSetEmplace(); + void TestHSetEmplaceNoresize(); + void TestHSetEmplaceDirect(); + void TestHMMapEmplace(); + void TestHMMapEmplaceNoresize(); + void TestHMMapEmplaceDirect(); + void TestNonCopyable(); + void TestValueInitialization(); + void TestAssignmentClear(); + void TestReleaseNodes(); + void TestAt(); + void TestHMapInitializerList(); + void TestHMMapInitializerList(); + void TestHSetInitializerList(); + void TestHMSetInitializerList(); + void TestHSetInsertInitializerList(); + void TestTupleHash(); + void TestStringHash(); + void TestFloatingPointHash(); +}; + +UNIT_TEST_SUITE_REGISTRATION(THashTest); + +void THashTest::TestHMapConstructorsAndAssignments() { + using container = THashMap; + + container c1; + c1["one"] = 1; + c1["two"] = 2; + + container c2(c1); + + UNIT_ASSERT_VALUES_EQUAL(2, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(1, c1.at("one")); /* Note: fails under MSVC since it does not support implicit generation of move constructors. */ + UNIT_ASSERT_VALUES_EQUAL(2, c2.at("two")); + + container c3(std::move(c1)); + + UNIT_ASSERT_VALUES_EQUAL(0, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c3.size()); + UNIT_ASSERT_VALUES_EQUAL(1, c3.at("one")); + + c2["three"] = 3; + c3 = c2; + + UNIT_ASSERT_VALUES_EQUAL(3, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(3, c3.size()); + UNIT_ASSERT_VALUES_EQUAL(3, c3.at("three")); + + c2["four"] = 4; + c3 = std::move(c2); + + UNIT_ASSERT_VALUES_EQUAL(0, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(4, c3.size()); + UNIT_ASSERT_VALUES_EQUAL(4, c3.at("four")); + + const container c4{ + {"one", 1}, + {"two", 2}, + {"three", 3}, + {"four", 4}, + }; + + UNIT_ASSERT_VALUES_EQUAL(4, c4.size()); + UNIT_ASSERT_VALUES_EQUAL(1, c4.at("one")); + UNIT_ASSERT_VALUES_EQUAL(2, c4.at("two")); + UNIT_ASSERT_VALUES_EQUAL(3, c4.at("three")); + UNIT_ASSERT_VALUES_EQUAL(4, c4.at("four")); + + // non-existent values must be zero-initialized + UNIT_ASSERT_VALUES_EQUAL(c1["nonexistent"], 0); +} + +void THashTest::TestHMap1() { + using maptype = THashMap, TEqualTo>; + maptype m; + // Store mappings between roman numerals and decimals. + m['l'] = "50"; + m['x'] = "20"; // Deliberate mistake. + m['v'] = "5"; + m['i'] = "1"; + UNIT_ASSERT(!strcmp(m['x'].c_str(), "20")); + m['x'] = "10"; // Correct mistake. + UNIT_ASSERT(!strcmp(m['x'].c_str(), "10")); + + UNIT_ASSERT(!m.contains('z')); + UNIT_ASSERT(!strcmp(m['z'].c_str(), "")); + UNIT_ASSERT(m.contains('z')); + + UNIT_ASSERT(m.count('z') == 1); + auto p = m.insert(std::pair('c', TString("100"))); + + UNIT_ASSERT(p.second); + + p = m.insert(std::pair('c', TString("100"))); + UNIT_ASSERT(!p.second); + + // Some iterators compare check, really compile time checks + maptype::iterator ite(m.begin()); + maptype::const_iterator cite(m.begin()); + cite = m.begin(); + maptype const& cm = m; + cite = cm.begin(); + + UNIT_ASSERT((maptype::const_iterator)ite == cite); + UNIT_ASSERT(!((maptype::const_iterator)ite != cite)); + UNIT_ASSERT(cite == (maptype::const_iterator)ite); + UNIT_ASSERT(!(cite != (maptype::const_iterator)ite)); +} + +void THashTest::TestHMapEqualityOperator() { + using container = THashMap; + + container base; + base["one"] = 1; + base["two"] = 2; + + container c1(base); + UNIT_ASSERT(c1 == base); + + container c2; + c2["two"] = 2; + c2["one"] = 1; + UNIT_ASSERT(c2 == base); + + c2["three"] = 3; + UNIT_ASSERT(c2 != base); + + container c3(base); + c3["one"] = 0; + UNIT_ASSERT(c3 != base); +} + +void THashTest::TestHMMapEqualityOperator() { + using container = THashMultiMap; + using value = container::value_type; + + container base; + base.insert(value("one", 1)); + base.insert(value("one", -1)); + base.insert(value("two", 2)); + + container c1(base); + UNIT_ASSERT(c1 == base); + + container c2; + c2.insert(value("two", 2)); + c2.insert(value("one", -1)); + c2.insert(value("one", 1)); + UNIT_ASSERT(c2 == base); + + c2.insert(value("three", 3)); + UNIT_ASSERT(c2 != base); + + container c3; + c3.insert(value("one", 0)); + c3.insert(value("one", -1)); + c3.insert(value("two", 2)); + UNIT_ASSERT(c3 != base); + + container c4; + c4.insert(value("one", 1)); + c4.insert(value("one", -1)); + c4.insert(value("one", 0)); + c4.insert(value("two", 2)); + UNIT_ASSERT(c3 != base); +} + +void THashTest::TestHMMapConstructorsAndAssignments() { + using container = THashMultiMap; + + container c1; + c1.insert(container::value_type("one", 1)); + c1.insert(container::value_type("two", 2)); + + container c2(c1); + + UNIT_ASSERT_VALUES_EQUAL(2, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c2.size()); + + container c3(std::move(c1)); + + UNIT_ASSERT_VALUES_EQUAL(0, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c3.size()); + + c2.insert(container::value_type("three", 3)); + c3 = c2; + + UNIT_ASSERT_VALUES_EQUAL(3, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(3, c3.size()); + + c2.insert(container::value_type("four", 4)); + c3 = std::move(c2); + + UNIT_ASSERT_VALUES_EQUAL(0, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(4, c3.size()); +} + +void THashTest::TestHMMap1() { + using mmap = THashMultiMap, TEqualTo>; + mmap m; + + UNIT_ASSERT(m.count('X') == 0); + m.insert(std::pair('X', 10)); // Standard way. + UNIT_ASSERT(m.count('X') == 1); + + m.insert(std::pair('X', 20)); // jbuck: standard way + UNIT_ASSERT(m.count('X') == 2); + + m.insert(std::pair('Y', 32)); // jbuck: standard way + mmap::iterator i = m.find('X'); // Find first match. + + UNIT_ASSERT((*i).first == 'X'); + UNIT_ASSERT((*i).second == 10); + ++i; + UNIT_ASSERT((*i).first == 'X'); + UNIT_ASSERT((*i).second == 20); + + i = m.find('Y'); + UNIT_ASSERT((*i).first == 'Y'); + UNIT_ASSERT((*i).second == 32); + + i = m.find('Z'); + UNIT_ASSERT(i == m.end()); + + size_t count = m.erase('X'); + UNIT_ASSERT(count == 2); + + // Some iterators compare check, really compile time checks + mmap::iterator ite(m.begin()); + mmap::const_iterator cite(m.begin()); + + UNIT_ASSERT((mmap::const_iterator)ite == cite); + UNIT_ASSERT(!((mmap::const_iterator)ite != cite)); + UNIT_ASSERT(cite == (mmap::const_iterator)ite); + UNIT_ASSERT(!(cite != (mmap::const_iterator)ite)); + + using HMapType = THashMultiMap; + HMapType hmap; + + // We fill the map to implicitely start a rehash. + for (size_t counter = 0; counter < 3077; ++counter) { + hmap.insert(HMapType::value_type(1, counter)); + } + + hmap.insert(HMapType::value_type(12325, 1)); + hmap.insert(HMapType::value_type(12325, 2)); + + UNIT_ASSERT(hmap.count(12325) == 2); + + // At this point 23 goes to the same bucket as 12325, it used to reveal a bug. + hmap.insert(HMapType::value_type(23, 0)); + + UNIT_ASSERT(hmap.count(12325) == 2); + + UNIT_ASSERT(hmap.bucket_count() > 3000); + for (size_t n = 0; n < 10; n++) { + hmap.clear(); + hmap.insert(HMapType::value_type(1, 2)); + } + UNIT_ASSERT(hmap.bucket_count() < 30); +} + +void THashTest::TestHMMapHas() { + using mmap = THashMultiMap, TEqualTo>; + mmap m; + m.insert(std::pair('X', 10)); + m.insert(std::pair('X', 20)); + m.insert(std::pair('Y', 32)); + UNIT_ASSERT(m.contains('X')); + UNIT_ASSERT(m.contains('Y')); + UNIT_ASSERT(!m.contains('Z')); +} + +void THashTest::TestHSetConstructorsAndAssignments() { + using container = THashSet; + + container c1; + c1.insert(100); + c1.insert(200); + + container c2(c1); + + UNIT_ASSERT_VALUES_EQUAL(2, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c2.size()); + UNIT_ASSERT(c1.contains(100)); + UNIT_ASSERT(c2.contains(200)); + + container c3(std::move(c1)); + + UNIT_ASSERT_VALUES_EQUAL(0, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c3.size()); + UNIT_ASSERT(c3.contains(100)); + + c2.insert(300); + c3 = c2; + + UNIT_ASSERT_VALUES_EQUAL(3, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(3, c3.size()); + UNIT_ASSERT(c3.contains(300)); + + c2.insert(400); + c3 = std::move(c2); + + UNIT_ASSERT_VALUES_EQUAL(0, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(4, c3.size()); + UNIT_ASSERT(c3.contains(400)); + + container c4 = {1, 2, 3}; + UNIT_ASSERT_VALUES_EQUAL(c4.size(), 3); + UNIT_ASSERT(c4.contains(1)); + UNIT_ASSERT(c4.contains(2)); + UNIT_ASSERT(c4.contains(3)); +} + +void THashTest::TestHSetSize() { + using container = THashSet; + + container c; + c.insert(100); + c.insert(200); + + UNIT_ASSERT_VALUES_EQUAL(2, c.size()); + + c.insert(200); + + UNIT_ASSERT_VALUES_EQUAL(2, c.size()); +} + +void THashTest::TestHSet2() { + THashSet, TEqualTo> s; + auto p = s.insert(42); + UNIT_ASSERT(p.second); + UNIT_ASSERT(*(p.first) == 42); + + p = s.insert(42); + UNIT_ASSERT(!p.second); +} + +void THashTest::TestHSetEqualityOperator() { + using container = THashSet; + + container base; + base.insert(1); + base.insert(2); + + container c1(base); + UNIT_ASSERT(c1 == base); + + c1.insert(1); + UNIT_ASSERT(c1 == base); + + c1.insert(3); + UNIT_ASSERT(c1 != base); + + container c2; + c2.insert(2); + c2.insert(1); + UNIT_ASSERT(c2 == base); + + container c3; + c3.insert(1); + UNIT_ASSERT(c3 != base); +} + +void THashTest::TestHMSetConstructorsAndAssignments() { + using container = THashMultiSet; + + container c1; + c1.insert(100); + c1.insert(200); + + container c2(c1); + + UNIT_ASSERT_VALUES_EQUAL(2, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c2.size()); + UNIT_ASSERT(c1.find(100) != c1.end()); + UNIT_ASSERT(c2.find(200) != c2.end()); + + container c3(std::move(c1)); + + UNIT_ASSERT_VALUES_EQUAL(0, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c3.size()); + UNIT_ASSERT(c3.find(100) != c3.end()); + + c2.insert(300); + c3 = c2; + + UNIT_ASSERT_VALUES_EQUAL(3, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(3, c3.size()); + UNIT_ASSERT(c3.find(300) != c3.end()); + + c2.insert(400); + c3 = std::move(c2); + + UNIT_ASSERT_VALUES_EQUAL(0, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(4, c3.size()); + UNIT_ASSERT(c3.find(400) != c3.end()); +} + +void THashTest::TestHMSetSize() { + using container = THashMultiSet; + + container c; + c.insert(100); + c.insert(200); + + UNIT_ASSERT_VALUES_EQUAL(2, c.size()); + + c.insert(200); + + UNIT_ASSERT_VALUES_EQUAL(3, c.size()); +} + +void THashTest::TestHMSet1() { + hmset s; + UNIT_ASSERT(s.count(star) == 0); + s.insert(star); + UNIT_ASSERT(s.count(star) == 1); + s.insert(star); + UNIT_ASSERT(s.count(star) == 2); + auto i = s.find(char(40)); + UNIT_ASSERT(i == s.end()); + + i = s.find(star); + UNIT_ASSERT(i != s.end()); + UNIT_ASSERT(*i == '*'); + UNIT_ASSERT(s.erase(star) == 2); +} + +void THashTest::TestHMSetEqualityOperator() { + using container = THashMultiSet; + + container base; + base.insert(1); + base.insert(1); + base.insert(2); + + container c1(base); + UNIT_ASSERT(c1 == base); + + c1.insert(1); + UNIT_ASSERT(!(c1 == base)); + + container c2; + c2.insert(2); + c2.insert(1); + c2.insert(1); + UNIT_ASSERT(c2 == base); + + container c3; + c3.insert(1); + c3.insert(2); + UNIT_ASSERT(!(c3 == base)); + + c3.insert(1); + UNIT_ASSERT(c3 == base); + + c3.insert(3); + UNIT_ASSERT(!(c3 == base)); +} + +void THashTest::TestHMSetEmplace() { + class TKey: public NTesting::TProbe { + public: + TKey(NTesting::TProbeState* state, int key) + : TProbe(state) + , Key_(key) + { + } + + operator size_t() const { + return THash()(Key_); + } + + bool operator==(const TKey& other) const { + return Key_ == other.Key_; + } + + private: + int Key_; + }; + + NTesting::TProbeState state; + + { + THashMultiSet c; + c.emplace(&state, 1); + c.emplace(&state, 1); + c.emplace(&state, 2); + + UNIT_ASSERT_EQUAL(state.CopyAssignments, 0); + UNIT_ASSERT_EQUAL(state.MoveAssignments, 0); + UNIT_ASSERT_EQUAL(state.Constructors, 3); + UNIT_ASSERT_EQUAL(state.MoveConstructors, 0); + + UNIT_ASSERT_EQUAL(c.count(TKey(&state, 1)), 2); + UNIT_ASSERT_EQUAL(c.count(TKey(&state, 2)), 1); + UNIT_ASSERT_EQUAL(c.count(TKey(&state, 3)), 0); + + UNIT_ASSERT_EQUAL(state.Constructors, 6); + UNIT_ASSERT_EQUAL(state.Destructors, 3); + } + + UNIT_ASSERT_EQUAL(state.CopyAssignments, 0); + UNIT_ASSERT_EQUAL(state.MoveAssignments, 0); + UNIT_ASSERT_EQUAL(state.CopyConstructors, 0); + UNIT_ASSERT_EQUAL(state.MoveConstructors, 0); + UNIT_ASSERT_EQUAL(state.Constructors, 6); + UNIT_ASSERT_EQUAL(state.Destructors, 6); +} + +void THashTest::TestInsertErase() { + using hmap = THashMap, TEqualTo>; + using val_type = hmap::value_type; + + { + hmap values; + + UNIT_ASSERT(values.insert(val_type("foo", 0)).second); + UNIT_ASSERT(values.insert(val_type("bar", 0)).second); + UNIT_ASSERT(values.insert(val_type("abc", 0)).second); + + UNIT_ASSERT(values.erase("foo") == 1); + UNIT_ASSERT(values.erase("bar") == 1); + UNIT_ASSERT(values.erase("abc") == 1); + } + + { + hmap values; + + UNIT_ASSERT(values.insert(val_type("foo", 0)).second); + UNIT_ASSERT(values.insert(val_type("bar", 0)).second); + UNIT_ASSERT(values.insert(val_type("abc", 0)).second); + + UNIT_ASSERT(values.erase("abc") == 1); + UNIT_ASSERT(values.erase("bar") == 1); + UNIT_ASSERT(values.erase("foo") == 1); + } +} + +namespace { + struct TItem: public TSimpleRefCount { + const TString Key; + const TString Value; + + TItem(const TString& key, const TString& value) + : Key(key) + , Value(value) + { + } + }; + + using TItemPtr = TIntrusivePtr; + + struct TSelectKey { + const TString& operator()(const TItemPtr& item) const { + return item->Key; + } + }; + + using TItemMapBase = THashTable< + TItemPtr, + TString, + THash, + TSelectKey, + TEqualTo, + std::allocator>; + + struct TItemMap: public TItemMapBase { + TItemMap() + : TItemMapBase(1, THash(), TEqualTo()) + { + } + + TItem& Add(const TString& key, const TString& value) { + insert_ctx ins; + iterator it = find_i(key, ins); + if (it == end()) { + it = insert_direct(new TItem(key, value), ins); + } + return **it; + } + }; +} + +void THashTest::TestResizeOnInsertSmartPtrBug() { + TItemMap map; + map.Add("key1", "value1"); + map.Add("key2", "value2"); + map.Add("key3", "value3"); + map.Add("key4", "value4"); + map.Add("key5", "value5"); + map.Add("key6", "value6"); + map.Add("key7", "value7"); + TItem& item = map.Add("key8", "value8"); + UNIT_ASSERT_EQUAL(item.Key, "key8"); + UNIT_ASSERT_EQUAL(item.Value, "value8"); +} + +template +static void EmptyAndInsertTest(typename T::value_type v) { + T c; + UNIT_ASSERT(!c); + c.insert(v); + UNIT_ASSERT(c); +} + +void THashTest::TestEmpty() { + EmptyAndInsertTest>(1); + EmptyAndInsertTest>(std::pair(1, 2)); + EmptyAndInsertTest>(std::pair(1, 2)); +} + +void THashTest::TestDefaultConstructor() { + THashSet set; + + UNIT_ASSERT(set.begin() == set.end()); + + UNIT_ASSERT(set.find(0) == set.end()); + + auto range = set.equal_range(0); + UNIT_ASSERT(range.first == range.second); +} + +void THashTest::TestSizeOf() { + /* This test checks that we don't waste memory when all functors passed to + * THashTable are empty. It does rely on knowledge of THashTable internals, + * so if those change, the test will have to be adjusted accordingly. */ + + size_t expectedSize = sizeof(uintptr_t) + 3 * sizeof(size_t); + + UNIT_ASSERT_VALUES_EQUAL(sizeof(THashMap), expectedSize); + UNIT_ASSERT_VALUES_EQUAL(sizeof(THashMap, std::pair>), expectedSize); +} + +void THashTest::TestInvariants() { + std::set reference_set; + THashSet set; + + for (int i = 0; i < 1000; i++) { + set.insert(i); + reference_set.insert(i); + } + UNIT_ASSERT_VALUES_EQUAL(set.size(), 1000); + + int count0 = 0; + for (int i = 0; i < 1000; i++) { + count0 += (set.find(i) != set.end()) ? 1 : 0; + } + UNIT_ASSERT_VALUES_EQUAL(count0, 1000); + + int count1 = 0; + for (auto pos = set.begin(); pos != set.end(); pos++) { + ++count1; + } + UNIT_ASSERT_VALUES_EQUAL(count1, 1000); + + int count2 = 0; + for (const int& value : set) { + count2 += (reference_set.find(value) != reference_set.end()) ? 1 : 0; + } + UNIT_ASSERT_VALUES_EQUAL(count2, 1000); +} + +struct TAllocatorCounters { + TAllocatorCounters() + : Allocations(0) + , Deallocations(0) + { + } + + ~TAllocatorCounters() { + std::allocator allocator; + + /* Release whatever was (intentionally) leaked. */ + for (const auto& chunk : Chunks) { + allocator.deallocate(static_cast(chunk.first), chunk.second); + } + } + + size_t Allocations; + size_t Deallocations; + TSet> Chunks; +}; + +template +class TCountingAllocator: public std::allocator { + using base_type = std::allocator; + +public: + using size_type = typename base_type::size_type; + + template + struct rebind { + using other = TCountingAllocator; + }; + + TCountingAllocator() + : Counters_(nullptr) + { + } + + TCountingAllocator(TAllocatorCounters* counters) + : Counters_(counters) + { + Y_ASSERT(counters); + } + + template + TCountingAllocator(const TCountingAllocator& other) + : Counters_(other.Counters) + { + } + + T* allocate(size_type n) { + auto result = base_type::allocate(n); + + if (Counters_) { + ++Counters_->Allocations; + Counters_->Chunks.emplace(result, n * sizeof(T)); + } + + return result; + } + + void deallocate(T* p, size_type n) { + if (Counters_) { + ++Counters_->Deallocations; + Counters_->Chunks.erase(std::make_pair(p, n * sizeof(T))); + } + + base_type::deallocate(p, n); + } + +private: + TAllocatorCounters* Counters_; +}; + +void THashTest::TestAllocation() { + TAllocatorCounters counters; + + using int_set = THashSet, TEqualTo, TCountingAllocator>; + + { + int_set set0(&counters); + int_set set1(set0); + set0.clear(); + int_set set2(&counters); + set2 = set1; + UNIT_ASSERT_VALUES_EQUAL(counters.Allocations, 0); /* Copying around null sets should not trigger allocations. */ + + set0.insert(0); + UNIT_ASSERT_VALUES_EQUAL(counters.Allocations, 2); /* One for buckets array, one for a new node. */ + + set0.clear(); + set1 = set0; + int_set set3(set0); + UNIT_ASSERT_VALUES_EQUAL(counters.Allocations, 2); /* Copying from an empty set with allocated buckets should not trigger allocations. */ + + for (int i = 0; i < 1000; i++) { + set0.insert(i); + } + size_t allocations = counters.Allocations; + set0.clear(); + UNIT_ASSERT_VALUES_EQUAL(counters.Allocations, allocations); /* clear() should not trigger allocations. */ + } + + UNIT_ASSERT_VALUES_EQUAL(counters.Allocations, counters.Deallocations); +} + +template +class TNonCopyableInt { +public: + explicit TNonCopyableInt(int) { + } + + TNonCopyableInt() = delete; + TNonCopyableInt(const TNonCopyableInt&) = delete; + TNonCopyableInt(TNonCopyable&&) = delete; + TNonCopyableInt& operator=(const TNonCopyable&) = delete; + TNonCopyableInt& operator=(TNonCopyable&&) = delete; + + operator int() const { + return Value; + } +}; + +void THashTest::TestInsertCopy() { + THashMap hash; + + /* Insertion should not make copies of the provided key. */ + hash[TNonCopyableInt<0>(0)] = 0; +} + +void THashTest::TestEmplace() { + using hash_t = THashMap>; + hash_t hash; + hash.emplace(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(0)); + auto it = hash.find(1); + UNIT_ASSERT_VALUES_EQUAL(static_cast(it->second), 0); +} + +void THashTest::TestEmplaceNoresize() { + using hash_t = THashMap>; + hash_t hash; + hash.reserve(1); + hash.emplace_noresize(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(0)); + auto it = hash.find(1); + UNIT_ASSERT_VALUES_EQUAL(static_cast(it->second), 0); +} + +void THashTest::TestEmplaceDirect() { + using hash_t = THashMap>; + hash_t hash; + hash_t::insert_ctx ins; + hash.find(1, ins); + hash.emplace_direct(ins, std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(0)); + auto it = hash.find(1); + UNIT_ASSERT_VALUES_EQUAL(static_cast(it->second), 0); +} + +void THashTest::TestTryEmplace() { + static unsigned counter = 0u; + + struct TCountConstruct { + explicit TCountConstruct(int v) + : value(v) + { + ++counter; + } + TCountConstruct(const TCountConstruct&) = delete; + int value; + }; + + THashMap hash; + { + // try_emplace does not copy key if key is rvalue + auto r = hash.try_emplace(TNonCopyableInt<0>(0), 1); + UNIT_ASSERT(r.second); + UNIT_ASSERT_VALUES_EQUAL(1, counter); + UNIT_ASSERT_VALUES_EQUAL(1, r.first->second.value); + } + { + auto r = hash.try_emplace(0, 2); + UNIT_ASSERT(!r.second); + UNIT_ASSERT_VALUES_EQUAL(1, counter); + UNIT_ASSERT_VALUES_EQUAL(1, r.first->second.value); + } +} + +void THashTest::TestTryEmplaceCopyKey() { + static unsigned counter = 0u; + + struct TCountCopy { + explicit TCountCopy(int i) + : Value(i) + { + } + TCountCopy(const TCountCopy& other) + : Value(other.Value) + { + ++counter; + } + + operator int() const { + return Value; + } + + int Value; + }; + + THashMap> hash; + TCountCopy key(1); + { + // try_emplace copy key if key is lvalue + auto r = hash.try_emplace(key, 1); + UNIT_ASSERT(r.second); + UNIT_ASSERT_VALUES_EQUAL(1, counter); + } + { + // no insert - no copy + auto r = hash.try_emplace(key, 2); + UNIT_ASSERT(!r.second); + UNIT_ASSERT_VALUES_EQUAL(1, counter); + } +} + +void THashTest::TestInsertOrAssign() { + static int constructorCounter = 0; + static int assignmentCounter = 0; + + struct TCountConstruct { + explicit TCountConstruct(int v) + : Value(v) + { + ++constructorCounter; + } + + TCountConstruct& operator=(int v) { + Value = v; + ++assignmentCounter; + return *this; + } + + TCountConstruct(const TCountConstruct&) = delete; + int Value; + }; + + THashMap hash; + { + auto r = hash.insert_or_assign(TNonCopyableInt<4>(4), 1); + UNIT_ASSERT(r.second); + UNIT_ASSERT_VALUES_EQUAL(1, hash.size()); + UNIT_ASSERT_VALUES_EQUAL(1, constructorCounter); + UNIT_ASSERT_VALUES_EQUAL(0, assignmentCounter); + UNIT_ASSERT_VALUES_EQUAL(1, r.first->second.Value); + } + { + auto r = hash.insert_or_assign(TNonCopyableInt<4>(4), 5); + UNIT_ASSERT(!r.second); + UNIT_ASSERT_VALUES_EQUAL(1, hash.size()); + UNIT_ASSERT_VALUES_EQUAL(1, constructorCounter); + UNIT_ASSERT_VALUES_EQUAL(1, assignmentCounter); + UNIT_ASSERT_VALUES_EQUAL(5, r.first->second.Value); + } + { + constexpr int iterations = 200; + for (int iteration = 0; iteration < iterations; ++iteration) { + hash.insert_or_assign(iteration, iteration); + } + UNIT_ASSERT_VALUES_EQUAL(iterations, hash.size()); + UNIT_ASSERT_VALUES_EQUAL(iterations, constructorCounter); + UNIT_ASSERT_VALUES_EQUAL(2, assignmentCounter); + UNIT_ASSERT_VALUES_EQUAL(4, hash.at(4).Value); + UNIT_ASSERT_VALUES_EQUAL(44, hash.at(44).Value); + } +} + +void THashTest::TestHMMapEmplace() { + using hash_t = THashMultiMap>; + hash_t hash; + hash.emplace(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(0)); + auto it = hash.find(1); + UNIT_ASSERT_VALUES_EQUAL(static_cast(it->second), 0); +} + +void THashTest::TestHMMapEmplaceNoresize() { + using hash_t = THashMultiMap>; + hash_t hash; + hash.reserve(1); + hash.emplace_noresize(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(0)); + auto it = hash.find(1); + UNIT_ASSERT_VALUES_EQUAL(static_cast(it->second), 0); +} + +void THashTest::TestHMMapEmplaceDirect() { + using hash_t = THashMultiMap>; + hash_t hash; + hash_t::insert_ctx ins; + hash.find(1, ins); + hash.emplace_direct(ins, std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(0)); + auto it = hash.find(1); + UNIT_ASSERT_VALUES_EQUAL(static_cast(it->second), 0); +} + +void THashTest::TestHSetEmplace() { + using hash_t = THashSet, THash, TEqualTo>; + hash_t hash; + UNIT_ASSERT(!hash.contains(0)); + hash.emplace(0); + UNIT_ASSERT(hash.contains(0)); + UNIT_ASSERT(!hash.contains(1)); +} + +void THashTest::TestHSetEmplaceNoresize() { + using hash_t = THashSet, THash, TEqualTo>; + hash_t hash; + hash.reserve(1); + UNIT_ASSERT(!hash.contains(0)); + hash.emplace_noresize(0); + UNIT_ASSERT(hash.contains(0)); + UNIT_ASSERT(!hash.contains(1)); +} + +void THashTest::TestHSetEmplaceDirect() { + using hash_t = THashSet, THash, TEqualTo>; + hash_t hash; + UNIT_ASSERT(!hash.contains(0)); + hash_t::insert_ctx ins; + hash.find(0, ins); + hash.emplace_direct(ins, 1); + UNIT_ASSERT(hash.contains(0)); + UNIT_ASSERT(!hash.contains(1)); +} + +void THashTest::TestNonCopyable() { + struct TValue: public TNonCopyable { + int value; + TValue(int _value = 0) + : value(_value) + { + } + operator int() { + return value; + } + }; + + THashMap hash; + hash.emplace(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(5)); + auto&& value = hash[1]; + UNIT_ASSERT_VALUES_EQUAL(static_cast(value), 5); + auto&& not_inserted = hash[2]; + UNIT_ASSERT_VALUES_EQUAL(static_cast(not_inserted), 0); +} + +void THashTest::TestValueInitialization() { + THashMap hash; + + int& value = hash[0]; + + /* Implicitly inserted values should be value-initialized. */ + UNIT_ASSERT_VALUES_EQUAL(value, 0); +} + +void THashTest::TestAssignmentClear() { + /* This one tests that assigning an empty hash resets the buckets array. + * See operator= for details. */ + + THashMap hash; + size_t emptyBucketCount = hash.bucket_count(); + + for (int i = 0; i < 100; i++) { + hash[i] = i; + } + + hash = THashMap(); + + UNIT_ASSERT_VALUES_EQUAL(hash.bucket_count(), emptyBucketCount); +} + +void THashTest::TestReleaseNodes() { + TAllocatorCounters counters; + using TIntSet = THashSet, TEqualTo, TCountingAllocator>; + + TIntSet set(&counters); + for (int i = 0; i < 3; i++) { + set.insert(i); + } + UNIT_ASSERT_VALUES_EQUAL(counters.Allocations, 4); + + set.release_nodes(); + UNIT_ASSERT_VALUES_EQUAL(counters.Allocations, 4); + UNIT_ASSERT_VALUES_EQUAL(set.size(), 0); + + for (int i = 10; i < 13; i++) { + set.insert(i); + } + UNIT_ASSERT_VALUES_EQUAL(counters.Allocations, 7); + UNIT_ASSERT(set.contains(10)); + UNIT_ASSERT(!set.contains(0)); + + set.basic_clear(); + UNIT_ASSERT_VALUES_EQUAL(counters.Deallocations, 3); + + TIntSet set2; + set2.release_nodes(); + set2.insert(1); + UNIT_ASSERT_VALUES_EQUAL(set2.size(), 1); +} + +void THashTest::TestAt() { +#define TEST_AT_THROWN_EXCEPTION(SRC_TYPE, DST_TYPE, KEY_TYPE, KEY, MESSAGE) \ + { \ + THashMap testMap; \ + try { \ + KEY_TYPE testKey = KEY; \ + testMap.at(testKey); \ + UNIT_ASSERT_C(false, "THashMap::at(\"" << KEY << "\") should throw"); \ + } catch (const yexception& e) { \ + UNIT_ASSERT_C(e.AsStrBuf().Contains(MESSAGE), "Incorrect exception description: got \"" << e.what() << "\", expected: \"" << MESSAGE << "\""); \ + } catch (...) { \ + UNIT_ASSERT_C(false, "THashMap::at(\"" << KEY << "\") should throw yexception"); \ + } \ + } + + TEST_AT_THROWN_EXCEPTION(TString, TString, TString, "111", "111"); + TEST_AT_THROWN_EXCEPTION(TString, TString, const TString, "111", "111"); + TEST_AT_THROWN_EXCEPTION(TString, TString, TStringBuf, "111", "111"); + TEST_AT_THROWN_EXCEPTION(TString, TString, const TStringBuf, "111", "111"); + TEST_AT_THROWN_EXCEPTION(TStringBuf, TStringBuf, const char*, "111", "111"); + TEST_AT_THROWN_EXCEPTION(int, int, short, 11, "11"); + TEST_AT_THROWN_EXCEPTION(int, int, int, -1, "-1"); + TEST_AT_THROWN_EXCEPTION(int, int, long, 111, "111"); + TEST_AT_THROWN_EXCEPTION(int, int, long long, -1000000000000ll, "-1000000000000"); + TEST_AT_THROWN_EXCEPTION(int, int, unsigned short, 11, "11"); + TEST_AT_THROWN_EXCEPTION(int, int, unsigned int, 2, "2"); + TEST_AT_THROWN_EXCEPTION(int, int, unsigned long, 131, "131"); + TEST_AT_THROWN_EXCEPTION(int, int, unsigned long long, 1000000000000ll, "1000000000000"); + + char key[] = {11, 12, 0, 1, 2, 11, 0}; + TEST_AT_THROWN_EXCEPTION(TString, TString, char*, key, "\\x0B\\x0C"); + TEST_AT_THROWN_EXCEPTION(TString, TString, TStringBuf, TStringBuf(key, sizeof(key) - 1), "\\x0B\\x0C\\0\\1\\2\\x0B"); + +#undef TEST_AT_THROWN_EXCEPTION +} + +void THashTest::TestHMapInitializerList() { + THashMap h1 = {{"foo", "bar"}, {"bar", "baz"}, {"baz", "qux"}}; + THashMap h2; + h2.insert(std::pair("foo", "bar")); + h2.insert(std::pair("bar", "baz")); + h2.insert(std::pair("baz", "qux")); + UNIT_ASSERT_EQUAL(h1, h2); +} + +void THashTest::TestHMMapInitializerList() { + THashMultiMap h1 = { + {"foo", "bar"}, + {"foo", "baz"}, + {"baz", "qux"}}; + THashMultiMap h2; + h2.insert(std::pair("foo", "bar")); + h2.insert(std::pair("foo", "baz")); + h2.insert(std::pair("baz", "qux")); + UNIT_ASSERT_EQUAL(h1, h2); +} + +void THashTest::TestHSetInitializerList() { + THashSet h1 = {"foo", "bar", "baz"}; + THashSet h2; + h2.insert("foo"); + h2.insert("bar"); + h2.insert("baz"); + UNIT_ASSERT_EQUAL(h1, h2); +} + +void THashTest::TestHMSetInitializerList() { + THashMultiSet h1 = {"foo", "foo", "bar", "baz"}; + THashMultiSet h2; + h2.insert("foo"); + h2.insert("foo"); + h2.insert("bar"); + h2.insert("baz"); + UNIT_ASSERT_EQUAL(h1, h2); +} + +namespace { + struct TFoo { + int A; + int B; + + bool operator==(const TFoo& o) const { + return A == o.A && B == o.B; + } + }; +} + +template <> +struct THash { + size_t operator()(const TFoo& v) const { + return v.A ^ v.B; + } +}; + +template <> +void Out(IOutputStream& o, const TFoo& v) { + o << '{' << v.A << ';' << v.B << '}'; +} + +void THashTest::TestHSetInsertInitializerList() { + { + const THashSet x = {1}; + THashSet y; + y.insert({1}); + UNIT_ASSERT_VALUES_EQUAL(x, y); + } + { + const THashSet x = {1, 2}; + THashSet y; + y.insert({1, 2}); + UNIT_ASSERT_VALUES_EQUAL(x, y); + } + { + const THashSet x = {1, 2, 3, 4, 5}; + THashSet y; + y.insert({ + 1, + 2, + 3, + 4, + 5, + }); + UNIT_ASSERT_VALUES_EQUAL(x, y); + } + { + const THashSet x = {{1, 2}}; + THashSet y; + y.insert({{1, 2}}); + UNIT_ASSERT_VALUES_EQUAL(x, y); + } + { + const THashSet x = {{1, 2}, {3, 4}}; + THashSet y; + y.insert({{1, 2}, {3, 4}}); + UNIT_ASSERT_VALUES_EQUAL(x, y); + } +} + +/* + * Sequence for MultiHash is reversed as it calculates hash as + * f(head:tail) = f(tail)xHash(head) + */ +void THashTest::TestTupleHash() { + std::tuple tuple{1, 3}; + UNIT_ASSERT_VALUES_EQUAL(THash()(tuple), MultiHash(3, 1)); + + /* + * This thing checks that we didn't break STL code + * See https://a.yandex-team.ru/arc/commit/2864838#comment-401 + * for example + */ + struct A { + A Foo(const std::tuple& v) { + return std::get(v); + } + }; +} + +void THashTest::TestStringHash() { + // Make sure that different THash<> variants behave in the same way + const size_t expected = ComputeHash(TString("hehe")); + UNIT_ASSERT_VALUES_EQUAL(ComputeHash("hehe"), expected); // char[5] + UNIT_ASSERT_VALUES_EQUAL(ComputeHash("hehe"sv), expected); // std::string_view + UNIT_ASSERT_VALUES_EQUAL(ComputeHash(TStringBuf("hehe")), expected); // TStringBuf + UNIT_ASSERT_VALUES_EQUAL(ComputeHash("hehe"), expected); // const char* +} + +template +static void TestFloatingPointHashImpl() { + const TFloat f = 0; + Y_ASSERT(f == -f); + THashSet set; + set.insert(f); + UNIT_ASSERT_C(set.contains(-f), TypeName()); + UNIT_ASSERT_VALUES_EQUAL_C(ComputeHash(f), ComputeHash(-f), TypeName()); + for (int i = 0; i < 5; ++i) { + set.insert(-TFloat(i)); + set.insert(+TFloat(i)); + } + UNIT_ASSERT_VALUES_EQUAL_C(set.size(), 9, TypeName()); +} + +void THashTest::TestFloatingPointHash() { + TestFloatingPointHashImpl(); + TestFloatingPointHashImpl(); + // TestFloatingPointHashImpl(); +} diff --git a/util/generic/hash_ut.pyx b/util/generic/hash_ut.pyx new file mode 100644 index 00000000000..ecf6dac2e68 --- /dev/null +++ b/util/generic/hash_ut.pyx @@ -0,0 +1,84 @@ +# cython: c_string_type=str, c_string_encoding=utf8 + +from util.generic.hash cimport THashMap +from util.generic.string cimport TString + +import pytest +import unittest + +from libcpp.pair cimport pair +from cython.operator cimport dereference as deref + + +def _check_convert(THashMap[TString, int] x): + return x + + +class TestHash(unittest.TestCase): + + def test_constructors_and_assignments(self): + cdef THashMap[TString, int] c1 + c1["one"] = 1 + c1["two"] = 2 + cdef THashMap[TString, int] c2 = THashMap[TString, int](c1) + self.assertEqual(2, c1.size()) + self.assertEqual(2, c2.size()) + self.assertEqual(1, c1.at("one")) + self.assertTrue(c1.contains("two")) + self.assertTrue(c2.contains("one")) + self.assertEqual(2, c2.at("two")) + c2["three"] = 3 + c1 = c2 + self.assertEqual(3, c1.size()) + self.assertEqual(3, c2.size()) + self.assertEqual(3, c1.at("three")) + + def test_equality_operator(self): + cdef THashMap[TString, int] base + base["one"] = 1 + base["two"] = 2 + + cdef THashMap[TString, int] c1 = THashMap[TString, int](base) + self.assertTrue(c1==base) + + cdef THashMap[TString, int] c2 + c2["one"] = 1 + c2["two"] = 2 + self.assertTrue(c2 == base) + + c2["three"] = 3 + self.assertTrue(c2 != base) + + cdef THashMap[TString, int] c3 = THashMap[TString, int](base) + c3["one"] = 0 + self.assertTrue(c3 != base) + + def test_insert_erase(self): + cdef THashMap[TString, int] tmp + self.assertTrue(tmp.insert(pair[TString, int]("one", 0)).second) + self.assertFalse(tmp.insert(pair[TString, int]("one", 1)).second) + self.assertTrue(tmp.insert(pair[TString, int]("two", 2)).second) + cdef TString one = "one" + cdef TString two = "two" + self.assertEqual(tmp.erase(one), 1) + self.assertEqual(tmp.erase(two), 1) + self.assertEqual(tmp.size(), 0) + self.assertTrue(tmp.empty()) + + def test_iterators_and_find(self): + cdef THashMap[TString, int] tmp + self.assertTrue(tmp.begin() == tmp.end()) + self.assertTrue(tmp.find("1") == tmp.end()) + tmp["1"] = 1 + self.assertTrue(tmp.begin() != tmp.end()) + cdef THashMap[TString, int].iterator it = tmp.find("1") + self.assertTrue(it != tmp.end()) + self.assertEqual(deref(it).second, 1) + + def test_convert(self): + src = {'foo': 1, 'bar': 42} + self.assertEqual(_check_convert(src), src) + + bad_src = {'foo': 1, 'bar': 'baz'} + with self.assertRaises(TypeError): + _check_convert(bad_src) diff --git a/util/generic/hide_ptr.cpp b/util/generic/hide_ptr.cpp new file mode 100644 index 00000000000..74eac028aa6 --- /dev/null +++ b/util/generic/hide_ptr.cpp @@ -0,0 +1,5 @@ +#include "hide_ptr.h" + +void* HidePointerOrigin(void* ptr) { + return ptr; +} diff --git a/util/generic/hide_ptr.h b/util/generic/hide_ptr.h new file mode 100644 index 00000000000..c734894bd72 --- /dev/null +++ b/util/generic/hide_ptr.h @@ -0,0 +1,3 @@ +#pragma once + +void* HidePointerOrigin(void*); diff --git a/util/generic/intrlist.cpp b/util/generic/intrlist.cpp new file mode 100644 index 00000000000..18a9250cc85 --- /dev/null +++ b/util/generic/intrlist.cpp @@ -0,0 +1 @@ +#include "intrlist.h" diff --git a/util/generic/intrlist.h b/util/generic/intrlist.h new file mode 100644 index 00000000000..2982e3a2815 --- /dev/null +++ b/util/generic/intrlist.h @@ -0,0 +1,893 @@ +#pragma once + +#include "utility.h" + +#include +#include + +struct TIntrusiveListDefaultTag {}; + +/* + * two-way linked list + */ +template +class TIntrusiveListItem { +private: + using TListItem = TIntrusiveListItem; + +public: + inline TIntrusiveListItem() noexcept + : Next_(this) + , Prev_(Next_) + { + } + + inline ~TIntrusiveListItem() { + Unlink(); + } + +public: + Y_PURE_FUNCTION inline bool Empty() const noexcept { + return (Prev_ == this) && (Next_ == this); + } + + inline void Unlink() noexcept { + if (Empty()) { + return; + } + + Prev_->SetNext(Next_); + Next_->SetPrev(Prev_); + + ResetItem(); + } + + inline void LinkBefore(TListItem* before) noexcept { + Unlink(); + LinkBeforeNoUnlink(before); + } + + inline void LinkBeforeNoUnlink(TListItem* before) noexcept { + TListItem* const after = before->Prev(); + + after->SetNext(this); + SetPrev(after); + SetNext(before); + before->SetPrev(this); + } + + inline void LinkBefore(TListItem& before) noexcept { + LinkBefore(&before); + } + + inline void LinkAfter(TListItem* after) noexcept { + Unlink(); + LinkBeforeNoUnlink(after->Next()); + } + + inline void LinkAfter(TListItem& after) noexcept { + LinkAfter(&after); + } + +public: + inline TListItem* Prev() noexcept { + return Prev_; + } + + inline const TListItem* Prev() const noexcept { + return Prev_; + } + + inline TListItem* Next() noexcept { + return Next_; + } + + inline const TListItem* Next() const noexcept { + return Next_; + } + +public: + inline void SetNext(TListItem* item) noexcept { + Next_ = item; + } + + inline void SetPrev(TListItem* item) noexcept { + Prev_ = item; + } + +public: + inline T* Node() noexcept { + return static_cast(this); + } + + inline const T* Node() const noexcept { + return static_cast(this); + } + +public: + // NB(arkady-e1ppa): These methods are used to implement + // intrusive lock-free algorithms which want to natively + // interact with TIntrusiveList. + // Assume that if you've used MutableNext/MutablePrev + // methods, you are not safe to use anything but + // MutableNext/MutablePrev/ResetItem methods until + // you call a ResetItem method. + + inline TListItem*& MutableNext() noexcept { + return Next_; + } + + inline TListItem*& MutablePrev() noexcept { + return Prev_; + } + + inline void ResetItem() noexcept { + Next_ = this; + Prev_ = Next_; + } + +private: + inline TIntrusiveListItem(const TIntrusiveListItem&) = delete; + inline TIntrusiveListItem& operator=(const TIntrusiveListItem&) = delete; + +private: + TListItem* Next_; + TListItem* Prev_; +}; + +template +class TIntrusiveList { +private: + using TListItem = TIntrusiveListItem; + + template + class TIteratorBase { + public: + using TItem = TListItem; + using TReference = TNode&; + using TPointer = TNode*; + + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = ptrdiff_t; + + using value_type = TNode; + using reference = TReference; + using pointer = TPointer; + + inline TIteratorBase() noexcept + : Item_(nullptr) + { + } + + template + inline TIteratorBase(const TIteratorBase& right) noexcept + : Item_(right.Item()) + { + } + + inline TIteratorBase(TItem* item) noexcept + : Item_(item) + { + } + + inline TItem* Item() const noexcept { + return Item_; + } + + inline void Next() noexcept { + Item_ = Item_->Next(); + } + + inline void Prev() noexcept { + Item_ = Item_->Prev(); + } + + template + inline bool operator==(const TIteratorBase& right) const noexcept { + return Item() == right.Item(); + } + + template + inline bool operator!=(const TIteratorBase& right) const noexcept { + return Item() != right.Item(); + } + + inline TIteratorBase& operator++() noexcept { + Next(); + + return *this; + } + + inline TIteratorBase operator++(int) noexcept { + TIteratorBase ret(*this); + + Next(); + + return ret; + } + + inline TIteratorBase& operator--() noexcept { + Prev(); + + return *this; + } + + inline TIteratorBase operator--(int) noexcept { + TIteratorBase ret(*this); + + Prev(); + + return ret; + } + + inline TReference operator*() const noexcept { + return *Item_->Node(); + } + + inline TPointer operator->() const noexcept { + return Item_->Node(); + } + + private: + TItem* Item_; + }; + + template + class TReverseIteratorBase { + public: + using TItem = typename TIterator::TItem; + using TReference = typename TIterator::TReference; + using TPointer = typename TIterator::TPointer; + + using iterator_category = typename TIterator::iterator_category; + using difference_type = typename TIterator::difference_type; + + using value_type = typename TIterator::value_type; + using reference = typename TIterator::reference; + using pointer = typename TIterator::pointer; + + inline TReverseIteratorBase() noexcept = default; + + template + inline TReverseIteratorBase(const TReverseIteratorBase& right) noexcept + : Current_(right.Base()) + { + } + + inline explicit TReverseIteratorBase(TIterator item) noexcept + : Current_(item) + { + } + + inline TIterator Base() const noexcept { + return Current_; + } + + inline TItem* Item() const noexcept { + TIterator ret = Current_; + + return (--ret).Item(); + } + + inline void Next() noexcept { + Current_.Prev(); + } + + inline void Prev() noexcept { + Current_.Next(); + } + + template + inline bool operator==(const TReverseIteratorBase& right) const noexcept { + return Base() == right.Base(); + } + + template + inline bool operator!=(const TReverseIteratorBase& right) const noexcept { + return Base() != right.Base(); + } + + inline TReverseIteratorBase& operator++() noexcept { + Next(); + + return *this; + } + + inline TReverseIteratorBase operator++(int) noexcept { + TReverseIteratorBase ret(*this); + + Next(); + + return ret; + } + + inline TReverseIteratorBase& operator--() noexcept { + Prev(); + + return *this; + } + + inline TReverseIteratorBase operator--(int) noexcept { + TReverseIteratorBase ret(*this); + + Prev(); + + return ret; + } + + inline TReference operator*() const noexcept { + TIterator ret = Current_; + + return *--ret; + } + + inline TPointer operator->() const noexcept { + TIterator ret = Current_; + + return &*--ret; + } + + private: + TIterator Current_; + }; + +public: + using TIterator = TIteratorBase; + using TConstIterator = TIteratorBase; + + using TReverseIterator = TReverseIteratorBase; + using TConstReverseIterator = TReverseIteratorBase; + + using iterator = TIterator; + using const_iterator = TConstIterator; + + using reverse_iterator = TReverseIterator; + using const_reverse_iterator = TConstReverseIterator; + +public: + inline void Swap(TIntrusiveList& right) noexcept { + TIntrusiveList temp; + + temp.Append(right); + Y_ASSERT(right.Empty()); + right.Append(*this); + Y_ASSERT(this->Empty()); + this->Append(temp); + Y_ASSERT(temp.Empty()); + } + +public: + inline TIntrusiveList() noexcept = default; + + inline ~TIntrusiveList() = default; + + inline TIntrusiveList(TIntrusiveList&& right) noexcept { + this->Swap(right); + } + + inline TIntrusiveList& operator=(TIntrusiveList&& rhs) noexcept { + this->Swap(rhs); + return *this; + } + + inline explicit operator bool() const noexcept { + return !Empty(); + } + + Y_PURE_FUNCTION inline bool Empty() const noexcept { + return End_.Empty(); + } + + inline size_t Size() const noexcept { + return std::distance(Begin(), End()); + } + + inline void Remove(TListItem* item) noexcept { + item->Unlink(); + } + + inline void Clear() noexcept { + End_.Unlink(); + } + +public: + inline TIterator Begin() noexcept { + return ++End(); + } + + inline TIterator End() noexcept { + return TIterator(&End_); + } + + inline TConstIterator Begin() const noexcept { + return ++End(); + } + + inline TConstIterator End() const noexcept { + return TConstIterator(&End_); + } + + inline TReverseIterator RBegin() noexcept { + return TReverseIterator(End()); + } + + inline TReverseIterator REnd() noexcept { + return TReverseIterator(Begin()); + } + + inline TConstReverseIterator RBegin() const noexcept { + return TConstReverseIterator(End()); + } + + inline TConstReverseIterator REnd() const noexcept { + return TConstReverseIterator(Begin()); + } + + inline TConstIterator CBegin() const noexcept { + return Begin(); + } + + inline TConstIterator CEnd() const noexcept { + return End(); + } + + inline TConstReverseIterator CRBegin() const noexcept { + return RBegin(); + } + + inline TConstReverseIterator CREnd() const noexcept { + return REnd(); + } + +public: + inline iterator begin() noexcept { + return Begin(); + } + + inline iterator end() noexcept { + return End(); + } + + inline const_iterator begin() const noexcept { + return Begin(); + } + + inline const_iterator end() const noexcept { + return End(); + } + + inline reverse_iterator rbegin() noexcept { + return RBegin(); + } + + inline reverse_iterator rend() noexcept { + return REnd(); + } + + inline const_iterator cbegin() const noexcept { + return CBegin(); + } + + inline const_iterator cend() const noexcept { + return CEnd(); + } + + inline const_reverse_iterator crbegin() const noexcept { + return CRBegin(); + } + + inline const_reverse_iterator crend() const noexcept { + return CREnd(); + } + +public: + inline T* Back() noexcept { + return End_.Prev()->Node(); + } + + inline T* Front() noexcept { + return End_.Next()->Node(); + } + + inline const T* Back() const noexcept { + return End_.Prev()->Node(); + } + + inline const T* Front() const noexcept { + return End_.Next()->Node(); + } + + inline void PushBack(TListItem* item) noexcept { + item->LinkBefore(End_); + } + + inline void PushFront(TListItem* item) noexcept { + item->LinkAfter(End_); + } + + inline T* PopBack() noexcept { + TListItem* const ret = End_.Prev(); + + ret->Unlink(); + + return ret->Node(); + } + + inline T* PopFront() noexcept { + TListItem* const ret = End_.Next(); + + ret->Unlink(); + + return ret->Node(); + } + + inline void Append(TIntrusiveList& list) noexcept { + Cut(list.Begin(), list.End(), End()); + } + + inline void Append(TIntrusiveList&& list) noexcept { + Append(list); + } + + inline static void Cut(TIterator begin, TIterator end, TIterator pasteBefore) noexcept { + if (begin == end) { + return; + } + + TListItem* const cutFront = begin.Item(); + TListItem* const gapBack = end.Item(); + + TListItem* const gapFront = cutFront->Prev(); + TListItem* const cutBack = gapBack->Prev(); + + gapFront->SetNext(gapBack); + gapBack->SetPrev(gapFront); + + TListItem* const pasteBack = pasteBefore.Item(); + TListItem* const pasteFront = pasteBack->Prev(); + + pasteFront->SetNext(cutFront); + cutFront->SetPrev(pasteFront); + + cutBack->SetNext(pasteBack); + pasteBack->SetPrev(cutBack); + } + +public: + template + inline void ForEach(TFunctor&& functor) { + TIterator i = Begin(); + + while (i != End()) { + functor(&*(i++)); + } + } + + template + inline void ForEach(TFunctor&& functor) const { + TConstIterator i = Begin(); + + while (i != End()) { + functor(&*(i++)); + } + } + + template + inline void QuickSort(TComparer&& comparer) { + if (Begin() == End() || ++Begin() == End()) { + return; + } + + T* const pivot = PopFront(); + TIntrusiveList bigger; + TIterator i = Begin(); + + while (i != End()) { + if (comparer(*pivot, *i)) { + bigger.PushBack(&*i++); + } else { + ++i; + } + } + + this->QuickSort(comparer); + bigger.QuickSort(comparer); + + PushBack(pivot); + Append(bigger); + } + +private: + inline TIntrusiveList(const TIntrusiveList&) = delete; + inline TIntrusiveList& operator=(const TIntrusiveList&) = delete; + +private: + TListItem End_; +}; + +template +class TIntrusiveListWithAutoDelete: public TIntrusiveList { +public: + using TIterator = typename TIntrusiveList::TIterator; + using TConstIterator = typename TIntrusiveList::TConstIterator; + + using TReverseIterator = typename TIntrusiveList::TReverseIterator; + using TConstReverseIterator = typename TIntrusiveList::TConstReverseIterator; + + using iterator = TIterator; + using const_iterator = TConstIterator; + + using reverse_iterator = TReverseIterator; + using const_reverse_iterator = TConstReverseIterator; + +public: + inline TIntrusiveListWithAutoDelete() noexcept = default; + + inline TIntrusiveListWithAutoDelete(TIntrusiveListWithAutoDelete&& right) noexcept + : TIntrusiveList(std::move(right)) + { + } + + inline ~TIntrusiveListWithAutoDelete() { + this->Clear(); + } + + TIntrusiveListWithAutoDelete& operator=(TIntrusiveListWithAutoDelete&& rhs) noexcept { + TIntrusiveList::operator=(std::move(rhs)); + return *this; + } + +public: + inline void Clear() noexcept { + this->ForEach([](auto* item) { + D::Destroy(item); + }); + } + + inline static void Cut(TIterator begin, TIterator end) noexcept { + TIntrusiveListWithAutoDelete temp; + Cut(begin, end, temp.End()); + } + + inline static void Cut(TIterator begin, TIterator end, TIterator pasteBefore) noexcept { + TIntrusiveList::Cut(begin, end, pasteBefore); + } +}; + +/* + * one-way linked list + */ +template +class TIntrusiveSListItem { +private: + using TListItem = TIntrusiveSListItem; + +public: + inline TIntrusiveSListItem() noexcept + : Next_(nullptr) + { + } + + inline ~TIntrusiveSListItem() = default; + + inline bool IsEnd() const noexcept { + return Next_ == nullptr; + } + + inline TListItem* Next() noexcept { + return Next_; + } + + inline const TListItem* Next() const noexcept { + return Next_; + } + + inline void SetNext(TListItem* item) noexcept { + Next_ = item; + } + +public: + inline T* Node() noexcept { + return static_cast(this); + } + + inline const T* Node() const noexcept { + return static_cast(this); + } + +private: + TListItem* Next_; +}; + +template +class TIntrusiveSList { +private: + using TListItem = TIntrusiveSListItem; + +public: + template + class TIteratorBase { + public: + using TItem = TListItem; + using TReference = TNode&; + using TPointer = TNode*; + + using difference_type = std::ptrdiff_t; + using value_type = TNode; + using pointer = TPointer; + using reference = TReference; + using iterator_category = std::forward_iterator_tag; + + inline TIteratorBase(TListItem* item) noexcept + : Item_(item) + { + } + + inline void Next() noexcept { + Item_ = Item_->Next(); + } + + inline bool operator==(const TIteratorBase& right) const noexcept { + return Item_ == right.Item_; + } + + inline bool operator!=(const TIteratorBase& right) const noexcept { + return Item_ != right.Item_; + } + + inline TIteratorBase& operator++() noexcept { + Next(); + + return *this; + } + + inline TIteratorBase operator++(int) noexcept { + TIteratorBase ret(*this); + + Next(); + + return ret; + } + + inline TNode& operator*() noexcept { + return *Item_->Node(); + } + + inline TNode* operator->() noexcept { + return Item_->Node(); + } + + private: + TListItem* Item_; + }; + +public: + using TIterator = TIteratorBase; + using TConstIterator = TIteratorBase; + + using iterator = TIterator; + using const_iterator = TConstIterator; + +public: + inline TIntrusiveSList() noexcept + : Begin_(nullptr) + { + } + + inline void Swap(TIntrusiveSList& right) noexcept { + DoSwap(Begin_, right.Begin_); + } + + inline explicit operator bool() const noexcept { + return !Empty(); + } + + Y_PURE_FUNCTION inline bool Empty() const noexcept { + return Begin_ == nullptr; + } + + inline size_t Size() const noexcept { + return std::distance(Begin(), End()); + } + + inline void Clear() noexcept { + Begin_ = nullptr; + } + + inline TIterator Begin() noexcept { + return TIterator(Begin_); + } + + inline TIterator End() noexcept { + return TIterator(nullptr); + } + + inline TConstIterator Begin() const noexcept { + return TConstIterator(Begin_); + } + + inline TConstIterator End() const noexcept { + return TConstIterator(nullptr); + } + + inline TConstIterator CBegin() const noexcept { + return Begin(); + } + + inline TConstIterator CEnd() const noexcept { + return End(); + } + + //compat methods + inline iterator begin() noexcept { + return Begin(); + } + + inline iterator end() noexcept { + return End(); + } + + inline const_iterator begin() const noexcept { + return Begin(); + } + + inline const_iterator end() const noexcept { + return End(); + } + + inline const_iterator cbegin() const noexcept { + return CBegin(); + } + + inline const_iterator cend() const noexcept { + return CEnd(); + } + + inline T* Front() noexcept { + Y_ASSERT(Begin_); + return Begin_->Node(); + } + + inline const T* Front() const noexcept { + Y_ASSERT(Begin_); + return Begin_->Node(); + } + + inline void PushFront(TListItem* item) noexcept { + item->SetNext(Begin_); + Begin_ = item; + } + + inline T* PopFront() noexcept { + Y_ASSERT(Begin_); + + TListItem* const ret = Begin_; + Begin_ = Begin_->Next(); + + return ret->Node(); + } + + inline void Reverse() noexcept { + TIntrusiveSList temp; + + while (!Empty()) { + temp.PushFront(PopFront()); + } + + this->Swap(temp); + } + + template + inline void ForEach(TFunctor&& functor) const noexcept(noexcept(functor(std::declval().Node()))) { + TListItem* i = Begin_; + + while (i) { + TListItem* const next = i->Next(); + functor(i->Node()); + i = next; + } + } + +private: + TListItem* Begin_; +}; diff --git a/util/generic/intrlist_ut.cpp b/util/generic/intrlist_ut.cpp new file mode 100644 index 00000000000..eff7cdf2eee --- /dev/null +++ b/util/generic/intrlist_ut.cpp @@ -0,0 +1,512 @@ +#include "intrlist.h" + +#include + +#include + +class TListTest: public TTestBase { + UNIT_TEST_SUITE(TListTest); + UNIT_TEST(TestIterate); + UNIT_TEST(TestRIterate); + UNIT_TEST(TestForEach); + UNIT_TEST(TestForEachWithDelete); + UNIT_TEST(TestSize); + UNIT_TEST(TestQuickSort); + UNIT_TEST(TestCut); + UNIT_TEST(TestAppend); + UNIT_TEST(TestMoveCtor); + UNIT_TEST(TestMoveOpEq); + UNIT_TEST(TestListWithAutoDelete); + UNIT_TEST(TestListWithAutoDeleteMoveCtor); + UNIT_TEST(TestListWithAutoDeleteMoveOpEq); + UNIT_TEST(TestListWithAutoDeleteClear); + UNIT_TEST(TestSecondTag); + UNIT_TEST_SUITE_END(); + +private: + void TestSize(); + void TestIterate(); + void TestRIterate(); + void TestForEach(); + void TestForEachWithDelete(); + void TestQuickSort(); + void TestCut(); + void TestAppend(); + void TestMoveCtor(); + void TestMoveOpEq(); + void TestListWithAutoDelete(); + void TestListWithAutoDeleteMoveCtor(); + void TestListWithAutoDeleteMoveOpEq(); + void TestListWithAutoDeleteClear(); + void TestSecondTag(); +}; + +UNIT_TEST_SUITE_REGISTRATION(TListTest); + +class TInt: public TIntrusiveListItem { +public: + inline TInt(int value) noexcept + : Value_(value) + { + } + + TInt(TInt&& rhs) noexcept + : Value_(rhs.Value_) + { + rhs.Value_ = 0xDEAD; + } + + TInt& operator=(TInt&& rhs) noexcept { + Value_ = rhs.Value_; + rhs.Value_ = 0xBEEF; + return *this; + } + + inline operator int&() noexcept { + return Value_; + } + + inline operator const int&() const noexcept { + return Value_; + } + +private: + int Value_; +}; + +class TMyList: public TIntrusiveList { +public: + inline TMyList(int count) { + while (count > 0) { + PushFront(new TInt(count--)); + } + } + + //TMyList(const TMyList& rhs) = default; + TMyList(TMyList&& rhs) noexcept = default; + + //operator=(const TMyList& rhs) = default; + TMyList& operator=(TMyList&& rhs) noexcept = default; + + inline ~TMyList() { + while (!Empty()) { + delete PopBack(); + } + } +}; + +struct TIntGreater: private TGreater { + inline bool operator()(const TInt& l, const TInt& r) const noexcept { + return TGreater::operator()(l, r); + } +}; + +void TListTest::TestQuickSort() { + TMyList l(1000); + size_t c = 0; + + l.QuickSort(TIntGreater()); + + UNIT_ASSERT_EQUAL(l.Size(), 1000); + + for (TMyList::TIterator it = l.Begin(); it != l.End(); ++it) { + UNIT_ASSERT_EQUAL(*it, int(1000 - c++)); + } +} + +void TListTest::TestSize() { + TMyList l(1024); + + UNIT_ASSERT_EQUAL(l.Size(), 1024); +} + +void TListTest::TestIterate() { + TMyList l(1000); + size_t c = 0; + + for (TMyList::TIterator it = l.Begin(); it != l.End(); ++it) { + ++c; + + UNIT_ASSERT_EQUAL(*it, (int)c); + } + + UNIT_ASSERT_EQUAL(c, 1000); +} + +void TListTest::TestRIterate() { + TMyList l(1000); + size_t c = 1000; + + UNIT_ASSERT_EQUAL(l.RBegin(), TMyList::TReverseIterator(l.End())); + UNIT_ASSERT_EQUAL(l.REnd(), TMyList::TReverseIterator(l.Begin())); + + for (TMyList::TReverseIterator it = l.RBegin(); it != l.REnd(); ++it) { + UNIT_ASSERT_EQUAL(*it, (int)c--); + } + + UNIT_ASSERT_EQUAL(c, 0); +} + +class TSum { +public: + inline TSum(size_t& sum) + : Sum_(sum) + { + } + + inline void operator()(const TInt* v) noexcept { + Sum_ += *v; + } + +private: + size_t& Sum_; +}; + +class TSumWithDelete { +public: + inline TSumWithDelete(size_t& sum) + : Sum_(sum) + { + } + + inline void operator()(TInt* v) noexcept { + if (*v % 2) { + Sum_ += *v; + } else { + delete v; + } + } + +private: + size_t& Sum_; +}; + +void TListTest::TestForEach() { + TMyList l(1000); + size_t sum = 0; + TSum functor(sum); + + l.ForEach(functor); + + UNIT_ASSERT_EQUAL(sum, 1000 * 1001 / 2); +} + +void TListTest::TestForEachWithDelete() { + TMyList l(1000); + size_t sum = 0; + TSumWithDelete functor(sum); + + l.ForEach(functor); + + UNIT_ASSERT_EQUAL(sum, 500 * 500 /*== n * (x + y * (n - 1) / 2), x == 1, y == 2*/); +} + +static void CheckIterationAfterCut(const TMyList& l, const TMyList& l2, size_t N, size_t M) { + size_t c = 0; + for (TMyList::TConstIterator it = l.Begin(); it != l.End(); ++it) { + ++c; + + UNIT_ASSERT_EQUAL(*it, (int)c); + } + + UNIT_ASSERT_EQUAL(c, M); + + for (TMyList::TConstIterator it = l2.Begin(); it != l2.End(); ++it) { + ++c; + + UNIT_ASSERT_EQUAL(*it, (int)c); + } + + UNIT_ASSERT_EQUAL(c, N); + + for (TMyList::TConstIterator it = l2.End(); it != l2.Begin(); --c) { + --it; + + UNIT_ASSERT_EQUAL(*it, (int)c); + } + + UNIT_ASSERT_EQUAL(c, M); + + for (TMyList::TConstIterator it = l.End(); it != l.Begin(); --c) { + --it; + + UNIT_ASSERT_EQUAL(*it, (int)c); + } + + UNIT_ASSERT_EQUAL(c, 0); +} + +static void TestCutFront(int N, int M) { + TMyList l(N); + TMyList l2(0); + + TMyList::TIterator it = l.Begin(); + for (int i = 0; i < M; ++i) { + ++it; + } + + TMyList::Cut(l.Begin(), it, l2.End()); + CheckIterationAfterCut(l2, l, N, M); +} + +static void TestCutBack(int N, int M) { + TMyList l(N); + TMyList l2(0); + + TMyList::TIterator it = l.Begin(); + for (int i = 0; i < M; ++i) { + ++it; + } + + TMyList::Cut(it, l.End(), l2.End()); + CheckIterationAfterCut(l, l2, N, M); +} + +void TListTest::TestCut() { + TestCutFront(1000, 500); + TestCutBack(1000, 500); + TestCutFront(1, 0); + TestCutBack(1, 0); + TestCutFront(1, 1); + TestCutBack(1, 1); + TestCutFront(2, 0); + TestCutBack(2, 0); + TestCutFront(2, 1); + TestCutBack(2, 1); + TestCutFront(2, 2); + TestCutBack(2, 2); +} + +static void CheckIterationAfterAppend(const TMyList& l, size_t N, size_t M) { + TMyList::TConstIterator it = l.Begin(); + + for (size_t i = 1; i <= N; ++i, ++it) { + UNIT_ASSERT_EQUAL((int)i, *it); + } + + for (size_t i = 1; i <= M; ++i, ++it) { + UNIT_ASSERT_EQUAL((int)i, *it); + } + + UNIT_ASSERT_EQUAL(it, l.End()); +} + +static void TestAppend(int N, int M) { + TMyList l(N); + TMyList l2(M); + l.Append(l2); + + UNIT_ASSERT(l2.Empty()); + CheckIterationAfterAppend(l, N, M); +} + +void TListTest::TestAppend() { + ::TestAppend(500, 500); + ::TestAppend(0, 0); + ::TestAppend(1, 0); + ::TestAppend(0, 1); + ::TestAppend(1, 1); +} + +template +static void CheckList(const TListType& lst) { + int i = 1; + for (typename TListType::TConstIterator it = lst.Begin(); it != lst.End(); ++it, ++i) { + UNIT_ASSERT_EQUAL(*it, i); + } +} + +void TListTest::TestMoveCtor() { + const int N{42}; + TMyList lst{N}; + UNIT_ASSERT(!lst.Empty()); + UNIT_ASSERT_EQUAL(lst.Size(), N); + + CheckList(lst); + TMyList nextLst{std::move(lst)}; + UNIT_ASSERT(lst.Empty()); + CheckList(nextLst); +} + +void TListTest::TestMoveOpEq() { + const int N{42}; + TMyList lst{N}; + UNIT_ASSERT(!lst.Empty()); + UNIT_ASSERT_EQUAL(lst.Size(), N); + CheckList(lst); + + const int M{2}; + TMyList nextLst(M); + UNIT_ASSERT(!nextLst.Empty()); + UNIT_ASSERT_EQUAL(nextLst.Size(), M); + CheckList(nextLst); + + nextLst = std::move(lst); + UNIT_ASSERT(!nextLst.Empty()); + UNIT_ASSERT_EQUAL(nextLst.Size(), N); + CheckList(nextLst); +} + +class TSelfCountingInt: public TIntrusiveListItem { +public: + TSelfCountingInt(int& counter, int value) noexcept + : Counter_(counter) + , Value_(value) + { + ++Counter_; + } + + TSelfCountingInt(TSelfCountingInt&& rhs) noexcept + : Counter_(rhs.Counter_) + , Value_(rhs.Value_) + { + rhs.Value_ = 0xDEAD; + } + + TSelfCountingInt& operator=(TSelfCountingInt&& rhs) noexcept { + Value_ = rhs.Value_; + rhs.Value_ = 0xBEEF; + return *this; + } + + ~TSelfCountingInt() noexcept { + --Counter_; + } + + inline operator int&() noexcept { + return Value_; + } + + inline operator const int&() const noexcept { + return Value_; + } + +private: + int& Counter_; + int Value_; +}; + +struct TSelfCountingIntDelete { + static void Destroy(TSelfCountingInt* i) noexcept { + delete i; + } +}; + +void TListTest::TestListWithAutoDelete() { + using TListType = TIntrusiveListWithAutoDelete; + int counter{0}; + { + TListType lst; + UNIT_ASSERT(lst.Empty()); + lst.PushFront(new TSelfCountingInt(counter, 2)); + UNIT_ASSERT_EQUAL(lst.Size(), 1); + UNIT_ASSERT_EQUAL(counter, 1); + lst.PushFront(new TSelfCountingInt(counter, 1)); + UNIT_ASSERT_EQUAL(lst.Size(), 2); + UNIT_ASSERT_EQUAL(counter, 2); + CheckList(lst); + } + + UNIT_ASSERT_EQUAL(counter, 0); +} + +void TListTest::TestListWithAutoDeleteMoveCtor() { + using TListType = TIntrusiveListWithAutoDelete; + int counter{0}; + { + TListType lst; + lst.PushFront(new TSelfCountingInt(counter, 2)); + lst.PushFront(new TSelfCountingInt(counter, 1)); + UNIT_ASSERT_EQUAL(lst.Size(), 2); + UNIT_ASSERT_EQUAL(counter, 2); + CheckList(lst); + + TListType nextList(std::move(lst)); + UNIT_ASSERT_EQUAL(nextList.Size(), 2); + CheckList(nextList); + UNIT_ASSERT_EQUAL(counter, 2); + } + + UNIT_ASSERT_EQUAL(counter, 0); +} + +void TListTest::TestListWithAutoDeleteMoveOpEq() { + using TListType = TIntrusiveListWithAutoDelete; + int counter{0}; + { + TListType lst; + lst.PushFront(new TSelfCountingInt(counter, 2)); + lst.PushFront(new TSelfCountingInt(counter, 1)); + UNIT_ASSERT_EQUAL(lst.Size(), 2); + UNIT_ASSERT_EQUAL(counter, 2); + CheckList(lst); + + TListType nextList; + UNIT_ASSERT(nextList.Empty()); + nextList = std::move(lst); + UNIT_ASSERT_EQUAL(nextList.Size(), 2); + CheckList(nextList); + UNIT_ASSERT_EQUAL(counter, 2); + } + + UNIT_ASSERT_EQUAL(counter, 0); +} + +void TListTest::TestListWithAutoDeleteClear() { + using TListType = TIntrusiveListWithAutoDelete; + int counter{0}; + { + TListType lst; + UNIT_ASSERT(lst.Empty()); + lst.PushFront(new TSelfCountingInt(counter, 2)); + UNIT_ASSERT_EQUAL(lst.Size(), 1); + UNIT_ASSERT_EQUAL(counter, 1); + lst.PushFront(new TSelfCountingInt(counter, 1)); + UNIT_ASSERT_EQUAL(lst.Size(), 2); + UNIT_ASSERT_EQUAL(counter, 2); + CheckList(lst); + + lst.Clear(); + UNIT_ASSERT(lst.Empty()); + UNIT_ASSERT_EQUAL(counter, 0); + lst.PushFront(new TSelfCountingInt(counter, 1)); + UNIT_ASSERT_EQUAL(lst.Size(), 1); + } + + UNIT_ASSERT_EQUAL(counter, 0); +} + +struct TSecondTag {}; + +class TDoubleNode + : public TInt, + public TIntrusiveListItem { +public: + TDoubleNode(int value) noexcept + : TInt(value) + { + } +}; + +void TListTest::TestSecondTag() { + TDoubleNode zero(0), one(1); + TIntrusiveList first; + TIntrusiveList second; + + first.PushFront(&zero); + first.PushFront(&one); + second.PushBack(&zero); + second.PushBack(&one); + + UNIT_ASSERT_EQUAL(*first.Front(), 1); + UNIT_ASSERT_EQUAL(*++first.Begin(), 0); + UNIT_ASSERT_EQUAL(*first.Back(), 0); + + UNIT_ASSERT_EQUAL(*second.Front(), 0); + UNIT_ASSERT_EQUAL(*++second.Begin(), 1); + UNIT_ASSERT_EQUAL(*second.Back(), 1); + + second.Remove(&zero); + UNIT_ASSERT_EQUAL(*second.Front(), 1); + UNIT_ASSERT_EQUAL(*first.Back(), 0); +} diff --git a/util/generic/is_in.cpp b/util/generic/is_in.cpp new file mode 100644 index 00000000000..4edfd1fe100 --- /dev/null +++ b/util/generic/is_in.cpp @@ -0,0 +1 @@ +#include "is_in.h" diff --git a/util/generic/is_in.h b/util/generic/is_in.h new file mode 100644 index 00000000000..643edba5964 --- /dev/null +++ b/util/generic/is_in.h @@ -0,0 +1,53 @@ +#pragma once + +#include "typetraits.h" + +#include +#include + +template +constexpr bool IsIn(I f, I l, const T& v); + +template +constexpr bool IsIn(const C& c, const T& e); + +namespace NIsInHelper { + Y_HAS_MEMBER(find, FindMethod); + Y_HAS_SUBTYPE(const_iterator, ConstIterator); + Y_HAS_SUBTYPE(key_type, KeyType); + + template + using TIsAssocCont = TConjunction, THasConstIterator, THasKeyType>; + + template + struct TIsInTraits { + static constexpr bool IsIn(const C& c, const T& e) { + using std::begin; + using std::end; + return ::IsIn(begin(c), end(c), e); + } + }; + + template + struct TIsInTraits { + static constexpr bool IsIn(const C& c, const T& e) { + return c.find(e) != c.end(); + } + }; +} + +template +constexpr bool IsIn(I f, I l, const T& v) { + return std::find(f, l, v) != l; +} + +template +constexpr bool IsIn(const C& c, const T& e) { + using namespace NIsInHelper; + return TIsInTraits::value>::IsIn(c, e); +} + +template +constexpr bool IsIn(std::initializer_list l, const U& e) { + return ::IsIn(l.begin(), l.end(), e); +} diff --git a/util/generic/is_in_ut.cpp b/util/generic/is_in_ut.cpp new file mode 100644 index 00000000000..763018a0885 --- /dev/null +++ b/util/generic/is_in_ut.cpp @@ -0,0 +1,117 @@ +#include + +#include "algorithm.h" +#include "hash.h" +#include "hash_multi_map.h" +#include "hash_set.h" +#include "is_in.h" +#include "map.h" +#include "set.h" +#include "strbuf.h" +#include "string.h" + +Y_UNIT_TEST_SUITE(TIsIn) { + template + void TestIsInWithCont(const T& elem) { + class TMapMock: public TCont { + public: + typename TCont::const_iterator find(const typename TCont::key_type& k) const { + ++FindCalled; + return TCont::find(k); + } + + typename TCont::iterator find(const typename TCont::key_type& k) { + ++FindCalled; + return TCont::find(k); + } + + mutable size_t FindCalled = 1; + }; + + TMapMock m; + m.insert(elem); + + // use more effective find method + UNIT_ASSERT(IsIn(m, "found")); + UNIT_ASSERT(m.FindCalled); + m.FindCalled = 0; + + UNIT_ASSERT(!IsIn(m, "not found")); + UNIT_ASSERT(m.FindCalled); + m.FindCalled = 0; + } + + Y_UNIT_TEST(IsInTest) { + TestIsInWithCont>(std::make_pair("found", "1")); + TestIsInWithCont>(std::make_pair("found", "1")); + TestIsInWithCont>(std::make_pair("found", "1")); + TestIsInWithCont>(std::make_pair("found", "1")); + + TestIsInWithCont>("found"); + TestIsInWithCont>("found"); + TestIsInWithCont>("found"); + TestIsInWithCont>("found"); + + // vector also compiles and works + TVector v; + v.push_back("found"); + UNIT_ASSERT(IsIn(v, "found")); + UNIT_ASSERT(!IsIn(v, "not found")); + + // iterators interface + UNIT_ASSERT(IsIn(v.begin(), v.end(), "found")); + UNIT_ASSERT(!IsIn(v.begin(), v.end(), "not found")); + + // Works with TString (it has find, but find is not used) + TString s = "found"; + UNIT_ASSERT(IsIn(s, 'f')); + UNIT_ASSERT(!IsIn(s, 'z')); + + TStringBuf b = "found"; + UNIT_ASSERT(IsIn(b, 'f')); + UNIT_ASSERT(!IsIn(b, 'z')); + } + + Y_UNIT_TEST(IsInInitListTest) { + const char* abc = "abc"; + const char* def = "def"; + + UNIT_ASSERT(IsIn({6, 2, 12}, 6)); + UNIT_ASSERT(IsIn({6, 2, 12}, 2)); + UNIT_ASSERT(!IsIn({6, 2, 12}, 7)); + UNIT_ASSERT(IsIn({6}, 6)); + UNIT_ASSERT(!IsIn({6}, 7)); + UNIT_ASSERT(!IsIn(std::initializer_list(), 6)); + UNIT_ASSERT(IsIn({TStringBuf("abc"), TStringBuf("def")}, TStringBuf("abc"))); + UNIT_ASSERT(IsIn({TStringBuf("abc"), TStringBuf("def")}, TStringBuf("def"))); + UNIT_ASSERT(IsIn({"abc", "def"}, TStringBuf("def"))); + UNIT_ASSERT(IsIn({abc, def}, def)); // direct pointer comparison + UNIT_ASSERT(!IsIn({TStringBuf("abc"), TStringBuf("def")}, TStringBuf("ghi"))); + UNIT_ASSERT(!IsIn({"abc", "def"}, TStringBuf("ghi"))); + UNIT_ASSERT(!IsIn({"abc", "def"}, TString("ghi"))); + + const TStringBuf str = "abc////"; + + UNIT_ASSERT(IsIn({"abc", "def"}, TStringBuf{str.data(), 3})); + } + + Y_UNIT_TEST(ConfOfTest) { + UNIT_ASSERT(IsIn({1, 2, 3}, 1)); + UNIT_ASSERT(!IsIn({1, 2, 3}, 4)); + + const TString b = "b"; + + UNIT_ASSERT(!IsIn({"a", "b", "c"}, b.data())); // compares pointers by value. Whether it's good or not. + UNIT_ASSERT(IsIn(TVector({"a", "b", "c"}), b.data())); + UNIT_ASSERT(IsIn(TVector({"a", "b", "c"}), "b")); + } + + Y_UNIT_TEST(IsInArrayTest) { + const TString array[] = {"a", "b", "d"}; + + UNIT_ASSERT(IsIn(array, "a")); + UNIT_ASSERT(IsIn(array, TString("b"))); + UNIT_ASSERT(!IsIn(array, "c")); + UNIT_ASSERT(IsIn(array, TStringBuf("d"))); + } +} diff --git a/util/generic/iterator.cpp b/util/generic/iterator.cpp new file mode 100644 index 00000000000..7c5c206cc36 --- /dev/null +++ b/util/generic/iterator.cpp @@ -0,0 +1 @@ +#include "iterator.h" diff --git a/util/generic/iterator.h b/util/generic/iterator.h new file mode 100644 index 00000000000..19e9d20976d --- /dev/null +++ b/util/generic/iterator.h @@ -0,0 +1,139 @@ +#pragma once + +#include +#include + +namespace NStlIterator { + template + class TProxy { + public: + TProxy() = default; + TProxy(T&& value) + : Value_(std::move(value)) + { + } + + const T* operator->() const noexcept { + return &Value_; + } + + const T& operator*() const noexcept { + return Value_; + } + + bool operator==(const TProxy& rhs) const { + return Value_ == rhs.Value_; + } + + private: + T Value_; + }; +} // namespace NStlIterator + +/** + * Range adaptor that turns a derived class with a Java-style iteration + * interface into an STL range. + * + * Derived class is expected to define: + * \code + * TSomething* Next(); + * \endcode + * + * `Next()` returning `nullptr` signals end of range. Note that you can also use + * pointer-like types instead of actual pointers (e.g. `TAtomicSharedPtr`). + * + * Since iteration state is stored inside the derived class, the resulting range + * is an input range (works for single pass algorithms only). Technically speaking, + * if you're returning a non-const pointer from `Next`, it can also work as an output range. + * + * Example usage: + * \code + * class TSquaresGenerator: public TInputRangeAdaptor { + * public: + * const double* Next() { + * Current_ = State_ * State_; + * State_ += 1.0; + * // Never return nullptr => we have infinite range! + * return &Current_; + * } + * + * private: + * double State_ = 0.0; + * double Current_ = 0.0; + * } + * \endcode + */ +template +class TInputRangeAdaptor { +public: // TODO: private + class TIterator { + public: + static constexpr bool IsNoexceptNext = noexcept(std::declval().Next()); + + using difference_type = std::ptrdiff_t; + using pointer = decltype(std::declval().Next()); + using reference = decltype(*std::declval().Next()); + using value_type = std::remove_cv_t>; + using iterator_category = std::input_iterator_tag; + + inline TIterator() noexcept + : Slave_(nullptr) + , Cur_() + { + } + + inline TIterator(TSlave* slave) noexcept(IsNoexceptNext) + : Slave_(slave) + , Cur_(Slave_->Next()) + { + } + + inline bool operator==(const TIterator& it) const noexcept { + return Cur_ == it.Cur_; + } + + inline bool operator!=(const TIterator& it) const noexcept { + return !(*this == it); + } + + inline pointer operator->() const noexcept { + return Cur_; + } + + inline reference operator*() const noexcept { + return *Cur_; + } + + inline TIterator& operator++() noexcept(IsNoexceptNext) { + Cur_ = Slave_->Next(); + + return *this; + } + + private: + TSlave* Slave_; + pointer Cur_; + }; + +public: + using const_iterator = TIterator; + using iterator = const_iterator; + + inline iterator begin() const noexcept(TIterator::IsNoexceptNext) { + return TIterator(const_cast(static_cast(this))); + } + + inline iterator end() const noexcept { + return TIterator(); + } +}; + +/** + * Transform given reverse iterator into forward iterator pointing to the same element. + * + * @see http://stackoverflow.com/a/1830240 + */ +template +auto ToForwardIterator(TIterator iter) { + return std::next(iter).base(); +} diff --git a/util/generic/iterator_range.cpp b/util/generic/iterator_range.cpp new file mode 100644 index 00000000000..bd8d7ff81bf --- /dev/null +++ b/util/generic/iterator_range.cpp @@ -0,0 +1 @@ +#include "iterator_range.h" diff --git a/util/generic/iterator_range.h b/util/generic/iterator_range.h new file mode 100644 index 00000000000..9f4d02da295 --- /dev/null +++ b/util/generic/iterator_range.h @@ -0,0 +1,104 @@ +#pragma once + +#include + +#include +#include + +template +struct TIteratorRange { + using TElement = std::remove_reference_t())>; + + TIteratorRange(TBegin begin, TEnd end) + : Begin_(begin) + , End_(end) + { + } + + TIteratorRange() + : TIteratorRange(TBegin{}, TEnd{}) + { + } + + TBegin begin() const { + return Begin_; + } + + TEnd end() const { + return End_; + } + + bool empty() const { + // because range based for requires exactly '!=' + return !(Begin_ != End_); + } + +private: + TBegin Begin_; + TEnd End_; +}; + +template +class TIteratorRange { +public: + using iterator = TIterator; + using const_iterator = TIterator; + using value_type = typename std::iterator_traits::value_type; + using reference = typename std::iterator_traits::reference; + using const_reference = typename std::iterator_traits::reference; + using difference_type = typename std::iterator_traits::difference_type; + using size_type = std::size_t; + + TIteratorRange() + : Begin_() + , End_() + { + } + + TIteratorRange(TIterator begin, TIterator end) + : Begin_(begin) + , End_(end) + { + } + + TIterator begin() const { + return Begin_; + } + + TIterator end() const { + return End_; + } + + Y_PURE_FUNCTION bool empty() const { + return Begin_ == End_; + } + + size_type size() const { + return End_ - Begin_; + } + + reference operator[](size_t at) const { + Y_ASSERT(at < size()); + + return *(Begin_ + at); + } + +private: + TIterator Begin_; + TIterator End_; +}; + +template +TIteratorRange MakeIteratorRange(TIterator begin, TIterator end) { + return TIteratorRange(begin, end); +} + +template +TIteratorRange MakeIteratorRange(const std::pair& range) { + return TIteratorRange(range.first, range.second); +} + +template +TIteratorRange MakeIteratorRange(TBegin begin, TEnd end) { + return {begin, end}; +} diff --git a/util/generic/iterator_range_ut.cpp b/util/generic/iterator_range_ut.cpp new file mode 100644 index 00000000000..a7e3670ae10 --- /dev/null +++ b/util/generic/iterator_range_ut.cpp @@ -0,0 +1,98 @@ +#include "iterator_range.h" + +#include +#include +#include + +Y_UNIT_TEST_SUITE(IteratorRange) { + Y_UNIT_TEST(DefaultConstructor) { + TIteratorRange range; + UNIT_ASSERT(range.empty()); + } + + Y_UNIT_TEST(DefaultConstructorSentinel) { + TIteratorRange range; + UNIT_ASSERT(range.empty()); + } + + Y_UNIT_TEST(RangeBasedForLoop) { + // compileability test + for (int i : TIteratorRange()) { + Y_UNUSED(i); + } + } + + Y_UNIT_TEST(RangeBasedForLoopSentinel) { + // compileability test + for (int i : TIteratorRange()) { + Y_UNUSED(i); + } + } + + Y_UNIT_TEST(Works) { + const int values[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto range = MakeIteratorRange(values, values + Y_ARRAY_SIZE(values)); + UNIT_ASSERT_VALUES_EQUAL(range.size(), Y_ARRAY_SIZE(values)); + UNIT_ASSERT(Equal(range.begin(), range.end(), values)); + UNIT_ASSERT(!range.empty()); + } + + Y_UNIT_TEST(WorksSentinel) { + struct TRangeSentinel { + }; + + struct TEnumerator { + ui32 operator*() const { + return Cur; + } + + void operator++() { + ++Cur; + } + + bool operator!=(const TRangeSentinel&) const { + return Cur < End; + } + + ui32 Cur; + ui32 End; + }; + + auto range = MakeIteratorRange(TEnumerator{0, 10}, TRangeSentinel{}); + UNIT_ASSERT(!range.empty()); + + ui32 i = 0; + for (auto j : range) { + UNIT_ASSERT_VALUES_EQUAL(j, i++); + } + UNIT_ASSERT_VALUES_EQUAL(i, 10); + } + + Y_UNIT_TEST(OperatorsAndReferences) { + TVector values{1, 2, 3, 4, 5}; + auto range = MakeIteratorRange(values.begin(), values.end()); + UNIT_ASSERT_VALUES_EQUAL(range[2], 3); + UNIT_ASSERT_VALUES_EQUAL(range[range[2]], 4); + *values.begin() = 100500; + UNIT_ASSERT_VALUES_EQUAL(range[0], 100500); + range[0] = 100501; + UNIT_ASSERT_VALUES_EQUAL(range[0], 100501); + + TVector valuesBool{false, true, false, false, false, false, true}; + auto rangeBVector = MakeIteratorRange(valuesBool.begin(), valuesBool.end()); + UNIT_ASSERT_VALUES_EQUAL(rangeBVector[1], true); + rangeBVector[0] = true; + valuesBool.back() = false; + UNIT_ASSERT_VALUES_EQUAL(rangeBVector[0], true); + UNIT_ASSERT_VALUES_EQUAL(rangeBVector[2], false); + UNIT_ASSERT_VALUES_EQUAL(rangeBVector[6], false); + } + + Y_UNIT_TEST(CanUseInAlgorithms) { + const int values[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto range = MakeIteratorRange(values, values + Y_ARRAY_SIZE(values)); + // more like compileability test + // we should be able to use TIteratorRange as a container parameter for standard algorithms + UNIT_ASSERT(AllOf(range, [](int x) { return x > 0; })); + } +} diff --git a/util/generic/iterator_ut.cpp b/util/generic/iterator_ut.cpp new file mode 100644 index 00000000000..00be19e10ec --- /dev/null +++ b/util/generic/iterator_ut.cpp @@ -0,0 +1,63 @@ +#include "iterator.h" + +#include + +Y_UNIT_TEST_SUITE(TIterator) { + Y_UNIT_TEST(ToForwardIteratorTest) { + TVector x = {1, 2}; + UNIT_ASSERT_VALUES_EQUAL(*std::prev(x.end()), *ToForwardIterator(x.rbegin())); + UNIT_ASSERT_VALUES_EQUAL(*ToForwardIterator(std::prev(x.rend())), *x.begin()); + } +} + +Y_UNIT_TEST_SUITE(TInputRangeAdaptor) { + class TSquaresGenerator: public TInputRangeAdaptor { + public: + const i64* Next() { + Current_ = State_ * State_; + ++State_; + // Never return nullptr => we have infinite range! + return &Current_; + } + + private: + i64 State_ = 0.0; + i64 Current_ = 0.0; + }; + + Y_UNIT_TEST(TSquaresGenerator) { + i64 cur = 0; + for (i64 sqr : TSquaresGenerator{}) { + UNIT_ASSERT_VALUES_EQUAL(cur * cur, sqr); + + if (++cur > 10) { + break; + } + } + } + + class TUrlPart: public TInputRangeAdaptor { + public: + TUrlPart(const TStringBuf& url) + : Url_(url) + { + } + + NStlIterator::TProxy Next() { + return Url_.NextTok('/'); + } + + private: + TStringBuf Url_; + }; + + Y_UNIT_TEST(TUrlPart) { + const TVector expected = {TStringBuf("yandex.ru"), TStringBuf("search?")}; + auto expected_part = expected.begin(); + for (const TStringBuf& part : TUrlPart(TStringBuf("yandex.ru/search?"))) { + UNIT_ASSERT_VALUES_EQUAL(part, *expected_part); + ++expected_part; + } + UNIT_ASSERT(expected_part == expected.end()); + } +} diff --git a/util/generic/lazy_value.cpp b/util/generic/lazy_value.cpp new file mode 100644 index 00000000000..e687ec1a59c --- /dev/null +++ b/util/generic/lazy_value.cpp @@ -0,0 +1 @@ +#include "lazy_value.h" diff --git a/util/generic/lazy_value.h b/util/generic/lazy_value.h new file mode 100644 index 00000000000..e54cdb31d0d --- /dev/null +++ b/util/generic/lazy_value.h @@ -0,0 +1,62 @@ +#pragma once + +#include "maybe.h" +#include "function.h" + +template +class TLazyValueBase { +public: + using TInitializer = std::function; + + TLazyValueBase() = default; + + TLazyValueBase(TInitializer initializer) + : Initializer(std::move(initializer)) + { + } + + bool WasLazilyInitialized() const noexcept { + return ValueHolder.Defined(); + } + + const T& GetRef() const { + if (!WasLazilyInitialized()) { + InitDefault(); + } + return *ValueHolder; + } + + const T& operator*() const { + return GetRef(); + } + + const T* operator->() const { + return &GetRef(); + } + + void InitDefault() const { + Y_ASSERT(Initializer); + ValueHolder = Initializer(); + } + +private: + mutable TMaybe ValueHolder; + TInitializer Initializer; +}; + +// we need this to get implicit construction TLazyValue from lambda +// and save default copy constructor and operator= for type TLazyValue +template +class TLazyValue: public TLazyValueBase { +public: + template + TLazyValue(TArgs&&... args) + : TLazyValueBase(std::forward(args)...) + { + } +}; + +template +TLazyValue> MakeLazy(F&& f) { + return {std::forward(f)}; +} diff --git a/util/generic/lazy_value_ut.cpp b/util/generic/lazy_value_ut.cpp new file mode 100644 index 00000000000..ff5a0156c53 --- /dev/null +++ b/util/generic/lazy_value_ut.cpp @@ -0,0 +1,157 @@ +#include "lazy_value.h" + +#include + +Y_UNIT_TEST_SUITE(TLazyValueTestSuite) { + Y_UNIT_TEST(TestLazyValue) { + TLazyValue value([]() { + return 5; + }); + UNIT_ASSERT(!value.WasLazilyInitialized()); + UNIT_ASSERT_EQUAL(*value, 5); + UNIT_ASSERT(value.WasLazilyInitialized()); + } + + Y_UNIT_TEST(TestLazyValueInitialization) { + TLazyValue value1([]() { return 5; }); + + TLazyValue value2 = []() { return 5; }; + + TLazyValue notInitialized{}; + + TLazyValue copy1(value1); + + copy1 = value2; + } + + Y_UNIT_TEST(TestLazyValueCopy) { + TLazyValue value([]() { return 5; }); + UNIT_ASSERT(!value.WasLazilyInitialized()); + + TLazyValue emptyCopy = value; + UNIT_ASSERT(!emptyCopy.WasLazilyInitialized()); + + UNIT_ASSERT_EQUAL(*emptyCopy, 5); + UNIT_ASSERT(emptyCopy.WasLazilyInitialized()); + UNIT_ASSERT(!value.WasLazilyInitialized()); + + UNIT_ASSERT_EQUAL(*value, 5); + + TLazyValue notEmptyCopy = value; + UNIT_ASSERT(notEmptyCopy.WasLazilyInitialized()); + UNIT_ASSERT_EQUAL(*notEmptyCopy, 5); + } + + struct TCopyCounter { + TCopyCounter(size_t& numCopies) + : NumCopies(&numCopies) + { + } + + TCopyCounter() = default; + + TCopyCounter(const TCopyCounter& other) + : NumCopies(other.NumCopies) + { + ++(*NumCopies); + } + + TCopyCounter(TCopyCounter&&) = default; + + TCopyCounter& operator=(const TCopyCounter& other) { + if (this == &other) { + return *this; + } + NumCopies = other.NumCopies; + ++(*NumCopies); + return *this; + } + + TCopyCounter& operator=(TCopyCounter&&) = default; + + size_t* NumCopies = nullptr; + }; + + Y_UNIT_TEST(TestLazyValueMoveValueInitialization) { + size_t numCopies = 0; + TCopyCounter counter{numCopies}; + TLazyValue value{[v = std::move(counter)]() mutable { return std::move(v); }}; + value.InitDefault(); + UNIT_ASSERT_EQUAL(numCopies, 0); + } + + Y_UNIT_TEST(TestLazyValueCopyValueInitialization) { + size_t numCopies = 0; + TCopyCounter counter{numCopies}; + TLazyValue value{[&counter]() { return counter; }}; + UNIT_ASSERT_EQUAL(numCopies, 0); + value.InitDefault(); + UNIT_ASSERT_EQUAL(numCopies, 1); + } + + class TValueProvider { + public: + static size_t CountParseDataCalled; + + TValueProvider() + : Data_([&] { return this->ParseData(); }) + { + } + + const TString& GetData() const { + return *Data_; + } + + private: + TLazyValue Data_; + + TString ParseData() { + CountParseDataCalled++; + return "hi"; + } + }; + + size_t TValueProvider::CountParseDataCalled = 0; + + Y_UNIT_TEST(TestValueProvider) { + TValueProvider provider; + + UNIT_ASSERT(provider.GetData() == "hi"); + } + + Y_UNIT_TEST(TestValueProviderCopy) { + TValueProvider provider; + provider.GetData(); + const auto countParsed = TValueProvider::CountParseDataCalled; + provider.GetData(); + UNIT_ASSERT_EQUAL(countParsed, TValueProvider::CountParseDataCalled); + + TValueProvider providerCopy; + providerCopy = provider; + providerCopy.GetData(); + UNIT_ASSERT_EQUAL(countParsed, TValueProvider::CountParseDataCalled); + } + + Y_UNIT_TEST(TestEmptyProviderCopy) { + TValueProvider provider; + TValueProvider copy(provider); + + const auto countParsed = TValueProvider::CountParseDataCalled; + provider.GetData(); + UNIT_ASSERT_EQUAL(countParsed + 1, TValueProvider::CountParseDataCalled); + copy.GetData(); + UNIT_ASSERT_EQUAL(countParsed + 2, TValueProvider::CountParseDataCalled); + const TValueProvider notEmptyCopy(copy); + notEmptyCopy.GetData(); + UNIT_ASSERT_EQUAL(countParsed + 2, TValueProvider::CountParseDataCalled); + } + + Y_UNIT_TEST(TestMakeLazy) { + auto lv = MakeLazy([] { + return 100500; + }); + UNIT_ASSERT(!lv.WasLazilyInitialized()); + UNIT_ASSERT(lv.GetRef() == 100500); + UNIT_ASSERT(lv.WasLazilyInitialized()); + } +} diff --git a/util/generic/list.cpp b/util/generic/list.cpp new file mode 100644 index 00000000000..471c2a14b77 --- /dev/null +++ b/util/generic/list.cpp @@ -0,0 +1 @@ +#include "list.h" diff --git a/util/generic/list.h b/util/generic/list.h new file mode 100644 index 00000000000..7b0b8ffc723 --- /dev/null +++ b/util/generic/list.h @@ -0,0 +1,22 @@ +#pragma once + +#include "fwd.h" + +#include + +#include +#include +#include +#include + +template +class TList: public std::list> { + using TBase = std::list>; + +public: + using TBase::TBase; + + inline explicit operator bool() const noexcept { + return !this->empty(); + } +}; diff --git a/util/generic/list.pxd b/util/generic/list.pxd new file mode 100644 index 00000000000..5f3d92eac1c --- /dev/null +++ b/util/generic/list.pxd @@ -0,0 +1,65 @@ +cdef extern from "util/generic/list.h": + cdef cppclass TList[T]: + TList() except + + TList(TList&) except + + TList(size_t, T&) except + + + cppclass iterator: + iterator() + iterator(iterator &) + T& operator*() + iterator operator++() + iterator operator--() + bint operator==(iterator) + bint operator!=(iterator) + cppclass reverse_iterator: + reverse_iterator() + reverse_iterator(iterator &) + T& operator*() + reverse_iterator operator++() + reverse_iterator operator--() + bint operator==(reverse_iterator) + bint operator!=(reverse_iterator) + cppclass const_iterator(iterator): + pass + cppclass const_reverse_iterator(reverse_iterator): + pass + bint operator==(TList&, TList&) + bint operator!=(TList&, TList&) + bint operator<(TList&, TList&) + bint operator>(TList&, TList&) + bint operator<=(TList&, TList&) + bint operator>=(TList&, TList&) + void assign(size_t, T&) + T& back() + iterator begin() + const_iterator const_begin "begin"() + void clear() + bint empty() + iterator end() + const_iterator const_end "end"() + iterator erase(iterator) + iterator erase(iterator, iterator) + T& front() + iterator insert(iterator, T&) + void insert(iterator, size_t, T&) + size_t max_size() + void merge(TList&) + void pop_back() + void pop_front() + void push_back(T&) + void push_front(T&) + reverse_iterator rbegin() + const_reverse_iterator const_rbegin "rbegin"() + void remove(T&) + reverse_iterator rend() + const_reverse_iterator const_rend "rend"() + void resize(size_t, T&) + void reverse() + size_t size() + void sort() + void swap(TList&) + void splice(iterator, TList&) + void splice(iterator, TList&, iterator) + void splice(iterator, TList&, iterator, iterator) + void unique() diff --git a/util/generic/list_ut.cpp b/util/generic/list_ut.cpp new file mode 100644 index 00000000000..9e60ecf01b4 --- /dev/null +++ b/util/generic/list_ut.cpp @@ -0,0 +1,14 @@ +#include "list.h" + +#include + +Y_UNIT_TEST_SUITE(TYListSuite) { + Y_UNIT_TEST(TestInitializerList) { + TList l = {3, 42, 6}; + TList expected; + expected.push_back(3); + expected.push_back(42); + expected.push_back(6); + UNIT_ASSERT_VALUES_EQUAL(l, expected); + } +} diff --git a/util/generic/list_ut.pyx b/util/generic/list_ut.pyx new file mode 100644 index 00000000000..129e5bc9b67 --- /dev/null +++ b/util/generic/list_ut.pyx @@ -0,0 +1,158 @@ +from util.generic.list cimport TList + +import unittest +from cython.operator cimport preincrement + + +class TestList(unittest.TestCase): + + def test_ctor1(self): + cdef TList[int] tmp = TList[int]() + self.assertEqual(tmp.size(), 0) + + def test_ctor2(self): + cdef TList[int] tmp = TList[int](10, 42) + self.assertEqual(tmp.size(), 10) + self.assertEqual(tmp.front(), 42) + + def test_ctor3(self): + cdef TList[int] tmp = TList[int](10, 42) + cdef TList[int] tmp2 = TList[int](tmp) + self.assertEqual(tmp2.size(), 10) + self.assertEqual(tmp2.front(), 42) + + def test_operator_assign(self): + cdef TList[int] tmp2 + tmp2.push_back(1) + tmp2.push_back(2) + + cdef TList[int] tmp3 + tmp3.push_back(1) + tmp3.push_back(3) + + tmp3 = tmp2 + + def test_compare(self): + cdef TList[int] tmp1 + tmp1.push_back(1) + tmp1.push_back(2) + + cdef TList[int] tmp2 + tmp2.push_back(1) + tmp2.push_back(2) + + cdef TList[int] tmp3 + tmp3.push_back(1) + tmp3.push_back(3) + + self.assertTrue(tmp1 == tmp2) + self.assertTrue(tmp1 != tmp3) + + self.assertTrue(tmp1 < tmp3) + self.assertTrue(tmp1 <= tmp3) + + self.assertTrue(tmp3 > tmp1) + self.assertTrue(tmp3 >= tmp1) + + def test_push_pop_back(self): + cdef TList[int] tmp + self.assertEqual(tmp.size(), 0) + + tmp.push_back(42) + self.assertEqual(tmp.size(), 1) + self.assertEqual(tmp.back(), 42) + + tmp.push_back(77) + self.assertEqual(tmp.size(), 2) + self.assertEqual(tmp.back(), 77) + + tmp.pop_back() + self.assertEqual(tmp.size(), 1) + self.assertEqual(tmp.back(), 42) + + tmp.pop_back() + self.assertEqual(tmp.size(), 0) + + def test_front(self): + cdef TList[int] tmp + tmp.push_back(42) + self.assertEqual(tmp.front(), 42) + + def test_empty(self): + cdef TList[int] tmp + self.assertTrue(tmp.empty()) + tmp.push_back(42) + self.assertFalse(tmp.empty()) + + def test_max_size(self): + cdef TList[int] tmp + self.assertTrue(tmp.max_size() > 0) + + def test_resize(self): + cdef TList[int] tmp + + tmp.resize(100, 42) + self.assertEqual(tmp.size(), 100) + self.assertEqual(tmp.front(), 42) + self.assertEqual(tmp.back(), 42) + + def test_iter(self): + cdef TList[int] tmp + tmp.push_back(1) + tmp.push_back(20) + tmp.push_back(300) + + self.assertEqual([i for i in tmp], [1, 20, 300]) + + def test_iterator(self): + cdef TList[int] tmp + + self.assertTrue(tmp.begin() == tmp.end()) + self.assertTrue(tmp.rbegin() == tmp.rend()) + self.assertTrue(tmp.const_begin() == tmp.const_end()) + self.assertTrue(tmp.const_rbegin() == tmp.const_rend()) + + tmp.push_back(1) + + self.assertTrue(tmp.begin() != tmp.end()) + self.assertTrue(tmp.rbegin() != tmp.rend()) + self.assertTrue(tmp.const_begin() != tmp.const_end()) + self.assertTrue(tmp.const_rbegin() != tmp.const_rend()) + + self.assertTrue(preincrement(tmp.begin()) == tmp.end()) + self.assertTrue(preincrement(tmp.rbegin()) == tmp.rend()) + self.assertTrue(preincrement(tmp.const_begin()) == tmp.const_end()) + self.assertTrue(preincrement(tmp.const_rbegin()) == tmp.const_rend()) + + def test_assign(self): + cdef TList[int] tmp + + tmp.assign(10, 42) + self.assertEqual(tmp.size(), 10) + self.assertEqual(tmp.front(), 42) + self.assertEqual(tmp.back(), 42) + + def test_insert(self): + cdef TList[int] tmp + tmp.push_back(1) + tmp.push_back(2) + tmp.push_back(3) + + tmp.insert(tmp.begin(), 8) + self.assertEqual([i for i in tmp], [8, 1, 2, 3]) + + tmp.insert(tmp.begin(), 2, 6) + self.assertEqual([i for i in tmp], [6, 6, 8, 1, 2, 3]) + + def test_erase(self): + cdef TList[int] tmp + tmp.push_back(1) + tmp.push_back(2) + tmp.push_back(3) + tmp.push_back(4) + + tmp.erase(preincrement(tmp.begin())) + self.assertEqual([i for i in tmp], [1, 3, 4]) + + tmp.erase(tmp.begin(), preincrement(preincrement(tmp.begin()))) + self.assertEqual([i for i in tmp], [4]) diff --git a/util/generic/map.cpp b/util/generic/map.cpp new file mode 100644 index 00000000000..b323fbb46d6 --- /dev/null +++ b/util/generic/map.cpp @@ -0,0 +1 @@ +#include "map.h" diff --git a/util/generic/map.h b/util/generic/map.h new file mode 100644 index 00000000000..13ff5cae7cf --- /dev/null +++ b/util/generic/map.h @@ -0,0 +1,36 @@ +#pragma once + +#include "fwd.h" +#include "mapfindptr.h" + +#include +#include + +#include +#include +#include +#include + +template +class TMap: public std::map>>, public TMapOps> { + using TBase = std::map>>; + +public: + using TBase::TBase; + + inline explicit operator bool() const noexcept { + return !this->empty(); + } +}; + +template +class TMultiMap: public std::multimap>> { + using TBase = std::multimap>>; + +public: + using TBase::TBase; + + inline explicit operator bool() const noexcept { + return !this->empty(); + } +}; diff --git a/util/generic/map.pxd b/util/generic/map.pxd new file mode 100644 index 00000000000..5c9fc32804e --- /dev/null +++ b/util/generic/map.pxd @@ -0,0 +1,46 @@ +from libcpp.pair cimport pair + +cdef extern from "util/generic/map.h" nogil: + cdef cppclass TMap[T, U]: + cppclass iterator: + pair[T, U]& operator*() + iterator operator++() + iterator operator--() + bint operator==(iterator) + bint operator!=(iterator) + + cppclass const_iterator(iterator): + pass + + TMap() except + + TMap(TMap&) except + + U& operator[](T&) + TMap& operator=(TMap&) + + bint operator==(TMap&) + bint operator!=(TMap&) + bint operator<(TMap&) + bint operator>(TMap&) + bint operator<=(TMap&) + bint operator>=(TMap&) + + U& at(T&) except + + iterator begin() + const_iterator const_begin "begin"() + void clear() + size_t count(T&) + bint empty() + iterator end() + const_iterator const_end "end"() + pair[iterator, iterator] equal_range(T&) + void erase(iterator) except + + void erase(iterator, iterator) except + + size_t erase(T&) + iterator find(T&) + bint contains(T&) + const_iterator const_find "find"(T&) + pair[iterator, bint] insert(pair[T, U]) # XXX pair[T,U]& + iterator insert(iterator, pair[T, U]) # XXX pair[T,U]& + size_t max_size() + size_t size() + void swap(TMap&) diff --git a/util/generic/map_ut.cpp b/util/generic/map_ut.cpp new file mode 100644 index 00000000000..79e832b024f --- /dev/null +++ b/util/generic/map_ut.cpp @@ -0,0 +1,496 @@ +#include "map.h" + +#include +#include +#include + +Y_UNIT_TEST_SUITE(TYMapTest) { + template + void DoTestMap1(TMap, TAlloc>& m); + + template + void DoTestMMap1(TMultiMap, TAlloc>& mm); + + Y_UNIT_TEST(TestMap1) { + { + TMap> m; + DoTestMap1(m); + } + { + TMemoryPool p(100); + TMap, TPoolAllocator> m(&p); + DoTestMap1(m); + } + } + + Y_UNIT_TEST(TestMMap1) { + { + TMultiMap> mm; + DoTestMMap1(mm); + } + { + TMemoryPool p(100); + TMultiMap, TPoolAllocator> mm(&p); + DoTestMMap1(mm); + } + } + + template + void DoTestMap1(TMap, TAlloc>& m) { + using maptype = TMap, TAlloc>; + // Store mappings between roman numerals and decimals. + m['l'] = 50; + m['x'] = 20; // Deliberate mistake. + m['v'] = 5; + m['i'] = 1; + + UNIT_ASSERT(m['x'] == 20); + m['x'] = 10; // Correct mistake. + UNIT_ASSERT(m['x'] == 10); + UNIT_ASSERT(m['z'] == 0); + + UNIT_ASSERT(m.count('z') == 1); + + std::pair p = m.insert(std::pair('c', 100)); + + UNIT_ASSERT(p.second); + UNIT_ASSERT(p.first != m.end()); + UNIT_ASSERT((*p.first).first == 'c'); + UNIT_ASSERT((*p.first).second == 100); + + p = m.insert(std::pair('c', 100)); + + UNIT_ASSERT(!p.second); // already existing pair + UNIT_ASSERT(p.first != m.end()); + UNIT_ASSERT((*p.first).first == 'c'); + UNIT_ASSERT((*p.first).second == 100); + } + + template + void DoTestMMap1(TMultiMap, TAlloc>& m) { + using mmap = TMultiMap, TAlloc>; + + UNIT_ASSERT(m.count('X') == 0); + + m.insert(std::pair('X', 10)); // Standard way. + UNIT_ASSERT(m.count('X') == 1); + + m.insert(std::pair('X', 20)); // jbuck: standard way + UNIT_ASSERT(m.count('X') == 2); + + m.insert(std::pair('Y', 32)); // jbuck: standard way + typename mmap::iterator i = m.find('X'); // Find first match. + ++i; + UNIT_ASSERT((*i).first == 'X'); + UNIT_ASSERT((*i).second == 20); + ++i; + UNIT_ASSERT((*i).first == 'Y'); + UNIT_ASSERT((*i).second == 32); + ++i; + UNIT_ASSERT(i == m.end()); + + size_t count = m.erase('X'); + UNIT_ASSERT(count == 2); + } + + Y_UNIT_TEST(TestMMap2) { + using pair_type = std::pair; + + pair_type p1(3, 'c'); + pair_type p2(6, 'f'); + pair_type p3(1, 'a'); + pair_type p4(2, 'b'); + pair_type p5(3, 'x'); + pair_type p6(6, 'f'); + + using mmap = TMultiMap>; + + pair_type array[] = { + p1, + p2, + p3, + p4, + p5, + p6}; + + mmap m(array + 0, array + 6); + mmap::iterator i; + i = m.lower_bound(3); + UNIT_ASSERT((*i).first == 3); + UNIT_ASSERT((*i).second == 'c'); + + i = m.upper_bound(3); + UNIT_ASSERT((*i).first == 6); + UNIT_ASSERT((*i).second == 'f'); + } + + Y_UNIT_TEST(TestIterators) { + using int_map = TMap>; + int_map imap; + + { + int_map::iterator ite(imap.begin()); + int_map::const_iterator cite(imap.begin()); + + UNIT_ASSERT(ite == cite); + UNIT_ASSERT(!(ite != cite)); + UNIT_ASSERT(cite == ite); + UNIT_ASSERT(!(cite != ite)); + } + + using mmap = TMultiMap>; + using pair_type = mmap::value_type; + + pair_type p1(3, 'c'); + pair_type p2(6, 'f'); + pair_type p3(1, 'a'); + pair_type p4(2, 'b'); + pair_type p5(3, 'x'); + pair_type p6(6, 'f'); + + pair_type array[] = { + p1, + p2, + p3, + p4, + p5, + p6}; + + mmap m(array + 0, array + 6); + + { + mmap::iterator ite(m.begin()); + mmap::const_iterator cite(m.begin()); + //test compare between const_iterator and iterator + UNIT_ASSERT(ite == cite); + UNIT_ASSERT(!(ite != cite)); + UNIT_ASSERT(cite == ite); + UNIT_ASSERT(!(cite != ite)); + } + + mmap::reverse_iterator ri = m.rbegin(); + + UNIT_ASSERT(ri != m.rend()); + UNIT_ASSERT(ri == m.rbegin()); + UNIT_ASSERT((*ri).first == 6); + UNIT_ASSERT((*ri++).second == 'f'); + UNIT_ASSERT((*ri).first == 6); + UNIT_ASSERT((*ri).second == 'f'); + + mmap const& cm = m; + mmap::const_reverse_iterator rci = cm.rbegin(); + + UNIT_ASSERT(rci != cm.rend()); + UNIT_ASSERT((*rci).first == 6); + UNIT_ASSERT((*rci++).second == 'f'); + UNIT_ASSERT((*rci).first == 6); + UNIT_ASSERT((*rci).second == 'f'); + } + + Y_UNIT_TEST(TestEqualRange) { + using maptype = TMap>; + + { + maptype m; + m['x'] = 10; + + std::pair ret; + ret = m.equal_range('x'); + UNIT_ASSERT(ret.first != ret.second); + UNIT_ASSERT((*(ret.first)).first == 'x'); + UNIT_ASSERT((*(ret.first)).second == 10); + UNIT_ASSERT(++(ret.first) == ret.second); + } + + { + { + maptype m; + + maptype::iterator i = m.lower_bound('x'); + UNIT_ASSERT(i == m.end()); + + i = m.upper_bound('x'); + UNIT_ASSERT(i == m.end()); + + std::pair ret; + ret = m.equal_range('x'); + UNIT_ASSERT(ret.first == ret.second); + UNIT_ASSERT(ret.first == m.end()); + } + + { + const maptype m; + std::pair ret; + ret = m.equal_range('x'); + UNIT_ASSERT(ret.first == ret.second); + UNIT_ASSERT(ret.first == m.end()); + } + } + } + + struct TKey { + TKey() + : m_data(0) + { + } + + explicit TKey(int data) + : m_data(data) + { + } + + int m_data; + }; + + struct TKeyCmp { + bool operator()(TKey lhs, TKey rhs) const { + return lhs.m_data < rhs.m_data; + } + + bool operator()(TKey lhs, int rhs) const { + return lhs.m_data < rhs; + } + + bool operator()(int lhs, TKey rhs) const { + return lhs < rhs.m_data; + } + + using is_transparent = void; + }; + + struct TKeyCmpPtr { + bool operator()(TKey const volatile* lhs, TKey const volatile* rhs) const { + return (*lhs).m_data < (*rhs).m_data; + } + + bool operator()(TKey const volatile* lhs, int rhs) const { + return (*lhs).m_data < rhs; + } + + bool operator()(int lhs, TKey const volatile* rhs) const { + return lhs < (*rhs).m_data; + } + + using is_transparent = void; + }; + + Y_UNIT_TEST(TestTemplateMethods) { + { + using Container = TMap; + using value = Container::value_type; + + Container cont; + + cont.insert(value(TKey(1), 1)); + cont.insert(value(TKey(2), 2)); + cont.insert(value(TKey(3), 3)); + cont.insert(value(TKey(4), 4)); + + UNIT_ASSERT(cont.count(TKey(1)) == 1); + UNIT_ASSERT(cont.count(1) == 1); + UNIT_ASSERT(cont.count(5) == 0); + + UNIT_ASSERT(cont.find(2) != cont.end()); + UNIT_ASSERT(cont.lower_bound(2) != cont.end()); + UNIT_ASSERT(cont.upper_bound(2) != cont.end()); + UNIT_ASSERT(cont.equal_range(2) != std::make_pair(cont.begin(), cont.end())); + + Container const& ccont = cont; + + UNIT_ASSERT(ccont.find(2) != ccont.end()); + UNIT_ASSERT(ccont.lower_bound(2) != ccont.end()); + UNIT_ASSERT(ccont.upper_bound(2) != ccont.end()); + UNIT_ASSERT(ccont.equal_range(2) != std::make_pair(ccont.end(), ccont.end())); + } + + { + using Container = TMap; + using value = Container::value_type; + + Container cont; + + TKey key1(1), key2(2), key3(3), key4(4); + + cont.insert(value(&key1, 1)); + cont.insert(value(&key2, 2)); + cont.insert(value(&key3, 3)); + cont.insert(value(&key4, 4)); + + UNIT_ASSERT(cont.count(1) == 1); + UNIT_ASSERT(cont.count(5) == 0); + + UNIT_ASSERT(cont.find(2) != cont.end()); + UNIT_ASSERT(cont.lower_bound(2) != cont.end()); + UNIT_ASSERT(cont.upper_bound(2) != cont.end()); + UNIT_ASSERT(cont.equal_range(2) != std::make_pair(cont.begin(), cont.end())); + + Container const& ccont = cont; + + UNIT_ASSERT(ccont.find(2) != ccont.end()); + UNIT_ASSERT(ccont.lower_bound(2) != ccont.end()); + UNIT_ASSERT(ccont.upper_bound(2) != ccont.end()); + UNIT_ASSERT(ccont.equal_range(2) != std::make_pair(ccont.begin(), ccont.end())); + } + + { + using Container = TMultiMap; + using value = Container::value_type; + + Container cont; + + cont.insert(value(TKey(1), 1)); + cont.insert(value(TKey(2), 2)); + cont.insert(value(TKey(3), 3)); + cont.insert(value(TKey(4), 4)); + + UNIT_ASSERT(cont.count(TKey(1)) == 1); + UNIT_ASSERT(cont.count(1) == 1); + UNIT_ASSERT(cont.count(5) == 0); + + UNIT_ASSERT(cont.find(2) != cont.end()); + UNIT_ASSERT(cont.lower_bound(2) != cont.end()); + UNIT_ASSERT(cont.upper_bound(2) != cont.end()); + UNIT_ASSERT(cont.equal_range(2) != std::make_pair(cont.begin(), cont.end())); + + Container const& ccont = cont; + + UNIT_ASSERT(ccont.find(2) != ccont.end()); + UNIT_ASSERT(ccont.lower_bound(2) != ccont.end()); + UNIT_ASSERT(ccont.upper_bound(2) != ccont.end()); + UNIT_ASSERT(ccont.equal_range(2) != std::make_pair(ccont.end(), ccont.end())); + } + + { + using Container = TMultiMap; + using value = Container::value_type; + + Container cont; + + TKey key1(1), key2(2), key3(3), key4(4); + + cont.insert(value(&key1, 1)); + cont.insert(value(&key2, 2)); + cont.insert(value(&key3, 3)); + cont.insert(value(&key4, 4)); + + UNIT_ASSERT(cont.count(1) == 1); + UNIT_ASSERT(cont.count(5) == 0); + + UNIT_ASSERT(cont.find(2) != cont.end()); + UNIT_ASSERT(cont.lower_bound(2) != cont.end()); + UNIT_ASSERT(cont.upper_bound(2) != cont.end()); + UNIT_ASSERT(cont.equal_range(2) != std::make_pair(cont.begin(), cont.end())); + + Container const& ccont = cont; + + UNIT_ASSERT(ccont.find(2) != ccont.end()); + UNIT_ASSERT(ccont.lower_bound(2) != ccont.end()); + UNIT_ASSERT(ccont.upper_bound(2) != ccont.end()); + UNIT_ASSERT(ccont.equal_range(2) != std::make_pair(ccont.begin(), ccont.end())); + } + } + + template + static void EmptyAndInsertTest(typename T::value_type v) { + T c; + UNIT_ASSERT(!c); + c.insert(v); + UNIT_ASSERT(c); + } + + Y_UNIT_TEST(TestEmpty) { + EmptyAndInsertTest>>(std::pair('a', 1)); + EmptyAndInsertTest>>(std::pair('a', 1)); + } + + struct TParametrizedKeyCmp { + bool Inverse; + + TParametrizedKeyCmp(bool inverse = false) + : Inverse(inverse) + { + } + + bool operator()(TKey lhs, TKey rhs) const { + if (Inverse) { + return lhs.m_data > rhs.m_data; + } else { + return lhs.m_data < rhs.m_data; + } + } + }; + + Y_UNIT_TEST(TestMoveComparator) { + using Container = TMultiMap; + + TParametrizedKeyCmp direct(false); + TParametrizedKeyCmp inverse(true); + + { + Container c(direct); + c = Container(inverse); + + c.insert(std::make_pair(TKey(1), 101)); + c.insert(std::make_pair(TKey(2), 102)); + c.insert(std::make_pair(TKey(3), 103)); + + TVector values; + for (auto& i : c) { + values.push_back(i.second); + } + + UNIT_ASSERT_VALUES_EQUAL(values.size(), 3); + UNIT_ASSERT_VALUES_EQUAL(values[0], 103); + UNIT_ASSERT_VALUES_EQUAL(values[1], 102); + UNIT_ASSERT_VALUES_EQUAL(values[2], 101); + } + } + + Y_UNIT_TEST(TestMapInitializerList) { + TMap m = { + {"one", 1}, + {"two", 2}, + {"three", 3}, + {"four", 4}, + }; + + UNIT_ASSERT_VALUES_EQUAL(m.size(), 4); + UNIT_ASSERT_VALUES_EQUAL(m["one"], 1); + UNIT_ASSERT_VALUES_EQUAL(m["two"], 2); + UNIT_ASSERT_VALUES_EQUAL(m["three"], 3); + UNIT_ASSERT_VALUES_EQUAL(m["four"], 4); + } + + Y_UNIT_TEST(TestMMapInitializerList) { + TMultiMap mm = { + {"one", 1}, + {"two", 2}, + {"two", -2}, + {"three", 3}, + }; + UNIT_ASSERT(mm.contains("two")); + TMultiMap expected; + expected.emplace("one", 1); + expected.emplace("two", 2); + expected.emplace("two", -2); + expected.emplace("three", 3); + UNIT_ASSERT_VALUES_EQUAL(mm, expected); + } + + Y_UNIT_TEST(TestMovePoolAlloc) { + using TMapInPool = TMap, TPoolAllocator>; + + TMemoryPool pool(1); + + TMapInPool m(&pool); + m.emplace(0, 1); + + UNIT_ASSERT(m.contains(0)); + UNIT_ASSERT_VALUES_EQUAL(1, m[0]); + + TMapInPool movedM = std::move(m); + + UNIT_ASSERT(movedM.contains(0)); + UNIT_ASSERT_VALUES_EQUAL(1, movedM[0]); + } +} diff --git a/util/generic/map_ut.pyx b/util/generic/map_ut.pyx new file mode 100644 index 00000000000..19dc97b1347 --- /dev/null +++ b/util/generic/map_ut.pyx @@ -0,0 +1,84 @@ +# cython: c_string_type=unicode, c_string_encoding=utf8 + +from util.generic.map cimport TMap +from util.generic.string cimport TString + +import pytest +import unittest + +from libcpp.pair cimport pair +from cython.operator cimport dereference as deref + + +def _check_convert(TMap[TString, int] x): + return x + + +class TestMap(unittest.TestCase): + + def test_constructors_and_assignments(self): + cdef TMap[TString, int] c1 + c1["one"] = 1 + c1["two"] = 2 + cdef TMap[TString, int] c2 = TMap[TString, int](c1) + self.assertEqual(2, c1.size()) + self.assertEqual(2, c2.size()) + self.assertEqual(1, c1.at("one")) + self.assertTrue(c1.contains("two")) + self.assertTrue(c2.contains("one")) + self.assertEqual(2, c2.at("two")) + c2["three"] = 3 + c1 = c2 + self.assertEqual(3, c1.size()) + self.assertEqual(3, c2.size()) + self.assertEqual(3, c1.at("three")) + + def test_equality_operator(self): + cdef TMap[TString, int] base + base["one"] = 1 + base["two"] = 2 + + cdef TMap[TString, int] c1 = TMap[TString, int](base) + self.assertTrue(c1==base) + + cdef TMap[TString, int] c2 + c2["one"] = 1 + c2["two"] = 2 + self.assertTrue(c2 == base) + + c2["three"] = 3 + self.assertTrue(c2 != base) + + cdef TMap[TString, int] c3 = TMap[TString, int](base) + c3["one"] = 0 + self.assertTrue(c3 != base) + + def test_insert_erase(self): + cdef TMap[TString, int] tmp + self.assertTrue(tmp.insert(pair[TString, int]("one", 0)).second) + self.assertFalse(tmp.insert(pair[TString, int]("one", 1)).second) + self.assertTrue(tmp.insert(pair[TString, int]("two", 2)).second) + cdef TString one = "one" + cdef TString two = "two" + self.assertEqual(tmp.erase(one), 1) + self.assertEqual(tmp.erase(two), 1) + self.assertEqual(tmp.size(), 0) + self.assertTrue(tmp.empty()) + + def test_iterators_and_find(self): + cdef TMap[TString, int] tmp + self.assertTrue(tmp.begin() == tmp.end()) + self.assertTrue(tmp.find("1") == tmp.end()) + tmp["1"] = 1 + self.assertTrue(tmp.begin() != tmp.end()) + cdef TMap[TString, int].iterator it = tmp.find("1") + self.assertTrue(it != tmp.end()) + self.assertEqual(deref(it).second, 1) + + def test_convert(self): + src = {'foo': 1, 'bar': 42} + self.assertEqual(_check_convert(src), src) + + bad_src = {'foo': 1, 'bar': 'baz'} + with self.assertRaises(TypeError): + _check_convert(bad_src) diff --git a/util/generic/mapfindptr.cpp b/util/generic/mapfindptr.cpp new file mode 100644 index 00000000000..5e3ff4b7790 --- /dev/null +++ b/util/generic/mapfindptr.cpp @@ -0,0 +1 @@ +#include "mapfindptr.h" diff --git a/util/generic/mapfindptr.h b/util/generic/mapfindptr.h new file mode 100644 index 00000000000..bb6dc617552 --- /dev/null +++ b/util/generic/mapfindptr.h @@ -0,0 +1,61 @@ +#pragma once + +#include + +#include +#include + +/** MapFindPtr usage: + +if (T* value = MapFindPtr(myMap, someKey) { + Cout << *value; +} + +*/ + +template +inline auto MapFindPtr(Map& map Y_LIFETIME_BOUND, const K& key) { + auto i = map.find(key); + + return (i == map.end() ? nullptr : &i->second); +} + +template +inline auto MapFindPtr(const Map& map Y_LIFETIME_BOUND, const K& key) { + auto i = map.find(key); + + return (i == map.end() ? nullptr : &i->second); +} + +/** helper for THashMap/TMap */ +template +struct TMapOps { + template + inline auto FindPtr(const K& key) Y_LIFETIME_BOUND { + return MapFindPtr(static_cast(*this), key); + } + + template + inline auto FindPtr(const K& key) const Y_LIFETIME_BOUND { + return MapFindPtr(static_cast(*this), key); + } + + template + inline auto Value(const K& key, DefaultValue&& defaultValue) const { + auto found = FindPtr(key); + return found ? *found : std::forward(defaultValue); + } + + template + inline const V& ValueRef(const K& key, V& defaultValue Y_LIFETIME_BOUND) const Y_LIFETIME_BOUND { + static_assert(std::is_same, typename Derived::mapped_type>::value, "Passed default value must have the same type as the underlying map's mapped_type."); + + if (auto found = FindPtr(key)) { + return *found; + } + return defaultValue; + } + + template + inline const V& ValueRef(const K& key, V&& defaultValue Y_LIFETIME_BOUND) const Y_LIFETIME_BOUND = delete; +}; diff --git a/util/generic/mapfindptr_ut.cpp b/util/generic/mapfindptr_ut.cpp new file mode 100644 index 00000000000..4178f3a0cc1 --- /dev/null +++ b/util/generic/mapfindptr_ut.cpp @@ -0,0 +1,68 @@ +#include "string.h" +#include "hash.h" + +#include + +#include + +#include "mapfindptr.h" + +Y_UNIT_TEST_SUITE(TMapFindPtrTest) { + struct TTestMap: std::map, TMapOps {}; + + Y_UNIT_TEST(TestDerivedClass) { + TTestMap a; + + a[42] = "cat"; + UNIT_ASSERT(a.FindPtr(42)); + UNIT_ASSERT_EQUAL(*a.FindPtr(42), "cat"); + UNIT_ASSERT_EQUAL(a.FindPtr(0), nullptr); + + //test mutation + if (TString* p = a.FindPtr(42)) { + *p = "dog"; + } + UNIT_ASSERT(a.FindPtr(42)); + UNIT_ASSERT_EQUAL(*a.FindPtr(42), "dog"); + + //test const-overloaded functions too + const TTestMap& b = a; + UNIT_ASSERT(b.FindPtr(42) && *b.FindPtr(42) == "dog"); + UNIT_ASSERT_EQUAL(b.FindPtr(0), nullptr); + + UNIT_ASSERT_STRINGS_EQUAL(b.Value(42, "cat"), "dog"); + UNIT_ASSERT_STRINGS_EQUAL(b.Value(0, "alien"), "alien"); + } + + Y_UNIT_TEST(TestTemplateFind) { + THashMap m; + + m[TString("x")] = 2; + + UNIT_ASSERT(m.FindPtr(TStringBuf("x"))); + UNIT_ASSERT_EQUAL(*m.FindPtr(TStringBuf("x")), 2); + } + + Y_UNIT_TEST(TestValue) { + TTestMap a; + + a[1] = "lol"; + + UNIT_ASSERT_VALUES_EQUAL(a.Value(1, "123"), "lol"); + UNIT_ASSERT_VALUES_EQUAL(a.Value(2, "123"), "123"); + UNIT_ASSERT_VALUES_EQUAL(a.Value(2, "123"sv), "123"sv); + } + + Y_UNIT_TEST(TestValueRef) { + TTestMap a; + + a[1] = "lol"; + + const TString str123 = "123"; + TString str1234 = "1234"; + + UNIT_ASSERT_VALUES_EQUAL(a.ValueRef(1, str123), "lol"); + UNIT_ASSERT_VALUES_EQUAL(a.ValueRef(2, str123), "123"); + UNIT_ASSERT_VALUES_EQUAL(a.ValueRef(3, str1234), "1234"); + } +} diff --git a/util/generic/maybe.cpp b/util/generic/maybe.cpp new file mode 100644 index 00000000000..c238002739f --- /dev/null +++ b/util/generic/maybe.cpp @@ -0,0 +1,16 @@ +#include "maybe.h" +#include + +[[noreturn]] void NMaybe::TPolicyUndefinedExcept::OnEmpty(const std::type_info& valueTypeInfo) { + ythrow yexception() << "TMaybe is empty, value type: "sv << TypeName(valueTypeInfo); +} + +[[noreturn]] void NMaybe::TPolicyUndefinedFail::OnEmpty(const std::type_info& valueTypeInfo) { + const TString typeName = TypeName(valueTypeInfo); + Y_ABORT("TMaybe is empty, value type: %s", typeName.c_str()); +} + +template <> +void Out(IOutputStream& o, const TNothing&) { + o << "(empty maybe)"; +} diff --git a/util/generic/maybe.h b/util/generic/maybe.h new file mode 100644 index 00000000000..ba15c7d7e44 --- /dev/null +++ b/util/generic/maybe.h @@ -0,0 +1,868 @@ +#pragma once + +#include + +#include "maybe_traits.h" +#include "yexception.h" + +#include +#include +#include +#include + +namespace NMaybe { + struct TPolicyUndefinedExcept { + [[noreturn]] static void OnEmpty(const std::type_info& valueTypeInfo); + }; + + struct TPolicyUndefinedFail { + [[noreturn]] static void OnEmpty(const std::type_info& valueTypeInfo); + }; +} + +struct TNothing { + explicit constexpr TNothing(int) noexcept { + } +}; + +constexpr TNothing NothingObject{0}; + +constexpr TNothing Nothing() noexcept { + return NothingObject; +} + +constexpr bool operator==(TNothing, TNothing) noexcept { + return true; +} + +template +class TMaybe: private TMaybeBase { +public: + using TInPlace = NMaybe::TInPlace; + +private: + static_assert(!std::is_same, TNothing>::value, + "Instantiation of TMaybe with a TNothing type is ill-formed"); + static_assert(!std::is_same, TInPlace>::value, + "Instantiation of TMaybe with a TInPlace type is ill-formed"); + static_assert(!std::is_reference::value, + "Instantiation of TMaybe with reference type is ill-formed"); + static_assert(!std::is_array::value, + "Instantiation of TMaybe with array type is ill-formed"); + static_assert(std::is_destructible::value, + "Instantiation of TMaybe with non-destructible type is ill-formed"); + + template + struct TConstructibleFromMaybeSomehow { + public: + static constexpr bool value = std::is_constructible&>::value || + std::is_constructible&>::value || + std::is_constructible&&>::value || + std::is_constructible&&>::value || + std::is_convertible&, T>::value || + std::is_convertible&, T>::value || + std::is_convertible&&, T>::value || + std::is_convertible&&, T>::value; + }; + + template + struct TAssignableFromMaybeSomehow { + public: + static constexpr bool value = TConstructibleFromMaybeSomehow::value || + std::is_assignable&>::value || + std::is_assignable&>::value || + std::is_assignable&&>::value || + std::is_assignable&&>::value; + }; + + template + struct TImplicitCopyCtor { + public: + static constexpr bool value = std::is_constructible::value && + std::is_convertible::value && + !TConstructibleFromMaybeSomehow::value; + }; + + template + struct TExplicitCopyCtor { + public: + static constexpr bool value = std::is_constructible::value && + !std::is_convertible::value && + !TConstructibleFromMaybeSomehow::value; + }; + + template + struct TImplicitMoveCtor { + public: + static constexpr bool value = std::is_constructible::value && + std::is_convertible::value && + !TConstructibleFromMaybeSomehow::value; + }; + + template + struct TExplicitMoveCtor { + public: + static constexpr bool value = std::is_constructible::value && + !std::is_convertible::value && + !TConstructibleFromMaybeSomehow::value; + }; + + template + struct TCopyAssignable { + public: + static constexpr bool value = std::is_constructible::value && + std::is_assignable::value && + !TAssignableFromMaybeSomehow::value; + }; + + template + struct TMoveAssignable { + public: + static constexpr bool value = std::is_constructible::value && + std::is_assignable::value && + !TAssignableFromMaybeSomehow::value; + }; + + template + struct TImplicitAnyCtor { + public: + using UDec = std::decay_t; + + static constexpr bool value = std::is_constructible::value && + std::is_convertible::value && + !std::is_same::value && + !std::is_same::value; + }; + + template + struct TExplicitAnyCtor { + public: + using UDec = std::decay_t; + static constexpr bool value = std::is_constructible::value && + !std::is_convertible::value && + !std::is_same::value && + !std::is_same::value; + }; + + template + struct TAssignableFromAny { + public: + using UDec = std::decay_t; + static constexpr bool value = !std::is_same::value && + std::is_constructible::value && + std::is_assignable::value && + (!std::is_scalar::value || !std::is_same::value); + }; + + template + struct TIsMaybe { + static constexpr bool value = false; + }; + + template + struct TIsMaybe> { + static constexpr bool value = true; + }; + + template + static auto Call(F&& f, Args&&... args) -> decltype(std::forward(f)(std::forward(args)...)); + + template + using TCallResult = std::remove_reference_t(), std::declval()...))>>; + + template + static constexpr F&& CheckReturnsMaybe(F&& f) { + using ReturnType = TCallResult; + static_assert(TIsMaybe::value, "Function must return TMaybe"); + return f; + } + + using TBase = TMaybeBase; + +public: + using value_type = T; + using TValueType = value_type; + + TMaybe() noexcept = default; + + constexpr TMaybe(const TMaybe&) = default; + constexpr TMaybe(TMaybe&&) = default; + + template + constexpr explicit TMaybe(TInPlace, Args&&... args) + : TBase(TInPlace{}, std::forward(args)...) + { + } + + template + constexpr explicit TMaybe(TInPlace, std::initializer_list il, TArgs&&... args) + : TBase(TInPlace{}, il, std::forward(args)...) + { + } + + constexpr TMaybe(TNothing) noexcept { + } + + template ::value>> + TMaybe(const TMaybe& right) { + if (right.Defined()) { + new (Data()) T(right.GetRef()); + this->Defined_ = true; + } + } + + template ::value, bool> = false> + explicit TMaybe(const TMaybe& right) { + if (right.Defined()) { + new (Data()) T(right.GetRef()); + this->Defined_ = true; + } + } + + template ::value>> + TMaybe(TMaybe&& right) noexcept(std::is_nothrow_constructible::value) { + if (right.Defined()) { + new (Data()) T(std::move(right.GetRef())); + this->Defined_ = true; + } + } + + template ::value, bool> = false> + explicit TMaybe(TMaybe&& right) noexcept(std::is_nothrow_constructible::value) { + if (right.Defined()) { + new (Data()) T(std::move(right.GetRef())); + this->Defined_ = true; + } + } + + template ::value>> + constexpr TMaybe(U&& right) + : TBase(TInPlace{}, std::forward(right)) + { + } + + template ::value, bool> = false> + constexpr explicit TMaybe(U&& right) + : TBase(TInPlace{}, std::forward(right)) + { + } + + ~TMaybe() = default; + + constexpr TMaybe& operator=(const TMaybe&) = default; + constexpr TMaybe& operator=(TMaybe&&) = default; + + TMaybe& operator=(TNothing) noexcept { + Clear(); + return *this; + } + + template + std::enable_if_t::value, TMaybe&> operator=(U&& right) { + if (Defined()) { + *Data() = std::forward(right); + } else { + Init(std::forward(right)); + } + return *this; + } + + template + std::enable_if_t::value, + TMaybe&> + operator=(const TMaybe& right) { + if (right.Defined()) { + if (Defined()) { + *Data() = right.GetRef(); + } else { + Init(right.GetRef()); + } + } else { + Clear(); + } + + return *this; + } + + template + std::enable_if_t::value, + TMaybe&> + operator=(TMaybe&& right) noexcept( + std::is_nothrow_assignable::value&& std::is_nothrow_constructible::value) + { + if (right.Defined()) { + if (Defined()) { + *Data() = std::move(right.GetRef()); + } else { + Init(std::move(right.GetRef())); + } + } else { + Clear(); + } + + return *this; + } + + template + T& ConstructInPlace(Args&&... args) { + Clear(); + Init(std::forward(args)...); + return *Data(); + } + + Y_REINITIALIZES_OBJECT void Clear() noexcept { + if (Defined()) { + this->Defined_ = false; + Data()->~T(); + } + } + + constexpr bool Defined() const noexcept { + return this->Defined_; + } + + Y_PURE_FUNCTION constexpr bool Empty() const noexcept { + return !Defined(); + } + + void CheckDefined() const { + if (Y_UNLIKELY(!Defined())) { + Policy::OnEmpty(typeid(TValueType)); + } + } + + const T* Get() const noexcept Y_LIFETIME_BOUND { + return Defined() ? Data() : nullptr; + } + + T* Get() noexcept Y_LIFETIME_BOUND { + return Defined() ? Data() : nullptr; + } + + constexpr const T& GetRef() const& Y_LIFETIME_BOUND { + CheckDefined(); + + return *Data(); + } + + constexpr T& GetRef() & Y_LIFETIME_BOUND { + CheckDefined(); + + return *Data(); + } + + constexpr const T&& GetRef() const&& Y_LIFETIME_BOUND { + CheckDefined(); + + return std::move(*Data()); + } + + constexpr T&& GetRef() && Y_LIFETIME_BOUND { + CheckDefined(); + + return std::move(*Data()); + } + + constexpr const T& operator*() const& Y_LIFETIME_BOUND { + return GetRef(); + } + + constexpr T& operator*() & Y_LIFETIME_BOUND { + return GetRef(); + } + + constexpr const T&& operator*() const&& Y_LIFETIME_BOUND { + return std::move(GetRef()); + } + + constexpr T&& operator*() && Y_LIFETIME_BOUND { + return std::move(GetRef()); + } + + constexpr const T* operator->() const Y_LIFETIME_BOUND { + return &GetRef(); + } + + constexpr T* operator->() Y_LIFETIME_BOUND { + return &GetRef(); + } + + constexpr const T& GetOrElse(const T& elseValue Y_LIFETIME_BOUND) const Y_LIFETIME_BOUND { + return Defined() ? *Data() : elseValue; + } + + constexpr T& GetOrElse(T& elseValue Y_LIFETIME_BOUND) Y_LIFETIME_BOUND { + return Defined() ? *Data() : elseValue; + } + + constexpr T&& GetOrElse(T&& elseValue Y_LIFETIME_BOUND) && Y_LIFETIME_BOUND { + return Defined() ? std::move(*Data()) : std::move(elseValue); + } + + constexpr const TMaybe& OrElse(const TMaybe& elseValue Y_LIFETIME_BOUND) const noexcept Y_LIFETIME_BOUND { + return Defined() ? *this : elseValue; + } + + constexpr TMaybe& OrElse(TMaybe& elseValue Y_LIFETIME_BOUND) Y_LIFETIME_BOUND { + return Defined() ? *this : elseValue; + } + + constexpr TMaybe&& OrElse(TMaybe&& elseValue Y_LIFETIME_BOUND) && Y_LIFETIME_BOUND { + return Defined() ? std::move(*this) : std::move(elseValue); + } + + template + constexpr auto AndThen(F&& func) & { + using ReturnType = TCallResult; + + if (Defined()) { + return std::forward(CheckReturnsMaybe(func))(*Data()); + } + + return ReturnType{}; + } + + template + constexpr auto AndThen(F&& func) const& { + using ReturnType = TCallResult; + + if (Defined()) { + return std::forward(CheckReturnsMaybe(func))(*Data()); + } + + return ReturnType{}; + } + + template + constexpr auto AndThen(F&& func) && { + using ReturnType = TCallResult; + + if (Defined()) { + return std::forward(CheckReturnsMaybe(func))(std::move(*Data())); + } + + return ReturnType{}; + } + + template + constexpr auto AndThen(F&& func) const&& { + using ReturnType = TCallResult; + + if (Defined()) { + return std::forward(CheckReturnsMaybe(func))(std::move(*Data())); + } + + return ReturnType{}; + } + + template + constexpr auto Transform(F&& func) & { + using ReturnType = TMaybe>; + + if (Defined()) { + return ReturnType(std::forward(func)(*Data())); + } + + return ReturnType{}; + } + + template + constexpr auto Transform(F&& func) const& { + using ReturnType = TMaybe>; + + if (Defined()) { + return ReturnType(std::forward(func)(*Data())); + } + + return ReturnType{}; + } + + template + constexpr auto Transform(F&& func) && { + using ReturnType = TMaybe>; + + if (Defined()) { + return ReturnType(std::forward(func)(std::move(*Data()))); + } + + return ReturnType{}; + } + + template + constexpr auto Transform(F&& func) const&& { + using ReturnType = TMaybe>; + + if (Defined()) { + return ReturnType(std::forward(func)(std::move(*Data()))); + } + + return ReturnType{}; + } + + template + constexpr TMaybe Or(F&& func) const& { + using ResultType = TCallResult; + static_assert(std::is_same::value, "Function must return TMaybe with the same type"); + + if (Defined()) { + return *this; + } + + return std::forward(func)(); + } + + template + constexpr TMaybe Or(F&& func) && { + using ResultType = TCallResult; + static_assert(std::is_same::value, "Function must return TMaybe with the same type"); + + if (Defined()) { + return std::move(*this); + } + + return std::forward(func)(); + } + + template + TMaybe Cast() const { + return Defined() ? TMaybe(*Data()) : TMaybe(); + } + + constexpr explicit operator bool() const noexcept { + return Defined(); + } + + void Save(IOutputStream* out) const { + const bool defined = Defined(); + + ::Save(out, defined); + + if (defined) { + ::Save(out, *Data()); + } + } + + void Load(IInputStream* in) { + bool defined; + + ::Load(in, defined); + + if (defined) { + if (!Defined()) { + ConstructInPlace(); + } + + ::Load(in, *Data()); + } else { + Clear(); + } + } + + void Swap(TMaybe& other) { + if (this->Defined_ == other.Defined_) { + if (this->Defined_) { + ::DoSwap(this->Data_, other.Data_); + } + } else { + if (this->Defined_) { + other.Init(std::move(this->Data_)); + this->Clear(); + } else { + this->Init(std::move(other.Data_)); + other.Clear(); + } + } + } + + void swap(TMaybe& other) { + Swap(other); + } + +private: + constexpr const T* Data() const noexcept Y_LIFETIME_BOUND { + return std::addressof(this->Data_); + } + + constexpr T* Data() noexcept Y_LIFETIME_BOUND { + return std::addressof(this->Data_); + } + + template + void Init(Args&&... args) { + new (Data()) T(std::forward(args)...); + this->Defined_ = true; + } +}; + +template +using TMaybeFail = TMaybe; + +template +constexpr TMaybe, TPolicy> MakeMaybe(T&& value) { + return TMaybe, TPolicy>(std::forward(value)); +} + +template +constexpr TMaybe MakeMaybe(TArgs&&... args) { + return TMaybe(typename TMaybe::TInPlace{}, std::forward(args)...); +} + +template +constexpr TMaybe MakeMaybe(std::initializer_list il, TArgs&&... args) { + return TMaybe(typename TMaybe::TInPlace{}, il, std::forward(args)...); +} + +template +void Swap(TMaybe& lhs, TMaybe& rhs) { + lhs.Swap(rhs); +} + +template +void swap(TMaybe& lhs, TMaybe& rhs) { + lhs.Swap(rhs); +} + +template +struct THash> { + constexpr size_t operator()(const TMaybe& data) const { + return (data.Defined()) ? THash()(data.GetRef()) : 42; + } +}; + +// Comparisons between TMaybe +template +constexpr bool operator==(const ::TMaybe& left, const ::TMaybe& right) { + return (static_cast(left) != static_cast(right)) + ? false + : ( + !static_cast(left) + ? true + : *left == *right); +} + +template +constexpr bool operator!=(const TMaybe& left, const TMaybe& right) { + return !(left == right); +} + +template +constexpr bool operator<(const TMaybe& left, const TMaybe& right) { + return (!static_cast(right)) + ? false + : ( + !static_cast(left) + ? true + : (*left < *right)); +} + +template +constexpr bool operator>(const TMaybe& left, const TMaybe& right) { + return right < left; +} + +template +constexpr bool operator<=(const TMaybe& left, const TMaybe& right) { + return !(right < left); +} + +template +constexpr bool operator>=(const TMaybe& left, const TMaybe& right) { + return !(left < right); +} + +// Comparisons with TNothing +template +constexpr bool operator==(const TMaybe& left, TNothing) noexcept { + return !static_cast(left); +} + +template +constexpr bool operator==(TNothing, const TMaybe& right) noexcept { + return !static_cast(right); +} + +template +constexpr bool operator!=(const TMaybe& left, TNothing) noexcept { + return static_cast(left); +} + +template +constexpr bool operator!=(TNothing, const TMaybe& right) noexcept { + return static_cast(right); +} + +template +constexpr bool operator<(const TMaybe&, TNothing) noexcept { + return false; +} + +template +constexpr bool operator<(TNothing, const TMaybe& right) noexcept { + return static_cast(right); +} + +template +constexpr bool operator<=(const TMaybe& left, TNothing) noexcept { + return !static_cast(left); +} + +template +constexpr bool operator<=(TNothing, const TMaybe&) noexcept { + return true; +} + +template +constexpr bool operator>(const TMaybe& left, TNothing) noexcept { + return static_cast(left); +} + +template +constexpr bool operator>(TNothing, const TMaybe&) noexcept { + return false; +} + +template +constexpr bool operator>=(const TMaybe&, TNothing) noexcept { + return true; +} + +template +constexpr bool operator>=(TNothing, const TMaybe& right) noexcept { + return !static_cast(right); +} + +// Comparisons with T + +template +constexpr bool operator==(const TMaybe& maybe, const T& value) { + return static_cast(maybe) ? *maybe == value : false; +} + +template +constexpr bool operator==(const T& value, const TMaybe& maybe) { + return static_cast(maybe) ? *maybe == value : false; +} + +template +constexpr bool operator!=(const TMaybe& maybe, const T& value) { + return static_cast(maybe) ? !(*maybe == value) : true; +} + +template +constexpr bool operator!=(const T& value, const TMaybe& maybe) { + return static_cast(maybe) ? !(*maybe == value) : true; +} + +template +constexpr bool operator<(const TMaybe& maybe, const T& value) { + return static_cast(maybe) ? std::less{}(*maybe, value) : true; +} + +template +constexpr bool operator<(const T& value, const TMaybe& maybe) { + return static_cast(maybe) ? std::less{}(value, *maybe) : false; +} + +template +constexpr bool operator<=(const TMaybe& maybe, const T& value) { + return !(maybe > value); +} + +template +constexpr bool operator<=(const T& value, const TMaybe& maybe) { + return !(value > maybe); +} + +template +constexpr bool operator>(const TMaybe& maybe, const T& value) { + return static_cast(maybe) ? value < maybe : false; +} + +template +constexpr bool operator>(const T& value, const TMaybe& maybe) { + return static_cast(maybe) ? maybe < value : true; +} + +template +constexpr bool operator>=(const TMaybe& maybe, const T& value) { + return !(maybe < value); +} + +template +constexpr bool operator>=(const T& value, const TMaybe& maybe) { + return !(value < maybe); +} + +// Comparison with values convertible to T + +template ::value, int> = 0> +constexpr bool operator==(const ::TMaybe& maybe, const U& value) { + return static_cast(maybe) ? *maybe == value : false; +} + +template ::value, int> = 0> +constexpr bool operator==(const U& value, const ::TMaybe& maybe) { + return static_cast(maybe) ? *maybe == value : false; +} + +template ::value, int> = 0> +constexpr bool operator!=(const TMaybe& maybe, const U& value) { + return static_cast(maybe) ? !(*maybe == value) : true; +} + +template ::value, int> = 0> +constexpr bool operator!=(const U& value, const TMaybe& maybe) { + return static_cast(maybe) ? !(*maybe == value) : true; +} + +template ::value, int> = 0> +constexpr bool operator<(const TMaybe& maybe, const U& value) { + return static_cast(maybe) ? std::less{}(*maybe, value) : true; +} + +template ::value, int> = 0> +constexpr bool operator<(const U& value, const TMaybe& maybe) { + return static_cast(maybe) ? std::less{}(value, *maybe) : false; +} + +template ::value, int> = 0> +constexpr bool operator<=(const TMaybe& maybe, const U& value) { + return !(maybe > value); +} + +template ::value, int> = 0> +constexpr bool operator<=(const U& value, const TMaybe& maybe) { + return !(value > maybe); +} + +template ::value, int> = 0> +constexpr bool operator>(const TMaybe& maybe, const U& value) { + return static_cast(maybe) ? value < maybe : false; +} + +template ::value, int> = 0> +constexpr bool operator>(const U& value, const TMaybe& maybe) { + return static_cast(maybe) ? maybe < value : true; +} + +template ::value, int> = 0> +constexpr bool operator>=(const TMaybe& maybe, const U& value) { + return !(maybe < value); +} + +template ::value, int> = 0> +constexpr bool operator>=(const U& value, const TMaybe& maybe) { + return !(value < maybe); +} + +class IOutputStream; + +template +inline IOutputStream& operator<<(IOutputStream& out, const TMaybe& maybe) { + if (maybe.Defined()) { + out << *maybe; + } else { + out << TStringBuf("(empty maybe)"); + } + return out; +} diff --git a/util/generic/maybe.pxd b/util/generic/maybe.pxd new file mode 100644 index 00000000000..15161dd2044 --- /dev/null +++ b/util/generic/maybe.pxd @@ -0,0 +1,35 @@ +cdef extern from "" nogil: + cdef cppclass TNothing: + pass + + cdef TNothing Nothing() + + cdef cppclass TMaybe[T]: + TMaybe(...) except + + + TMaybe& operator=(...) except + + + void ConstructInPlace(...) except + + void Clear() except + + + bint Defined() + bint Empty() + + void CheckDefined() except + + + T* Get() except + + T& GetRef() except + + + T GetOrElse(T&) except + + TMaybe OrElse(TMaybe&) except + + + TMaybe[U] Cast[U]() except + + + void Swap(TMaybe& other) except + + + bint operator ==[U](U&) except + + bint operator !=[U](U&) except + + bint operator <[U](U&) except + + bint operator >[U](U&) except + + bint operator <=[U](U&) except + + bint operator >=[U](U&) except + diff --git a/util/generic/maybe_traits.h b/util/generic/maybe_traits.h new file mode 100644 index 00000000000..702912c8fa1 --- /dev/null +++ b/util/generic/maybe_traits.h @@ -0,0 +1,185 @@ +#pragma once + +#include +#include +#include + +namespace NMaybe { + struct TInPlace {}; + + template ::value> + struct TStorageBase { + constexpr TStorageBase() noexcept + : NullState_('\0') + { + } + + template + constexpr TStorageBase(TInPlace, Args&&... args) + : Data_(std::forward(args)...) + , Defined_(true) + { + } + + constexpr TStorageBase(TStorageBase&&) = default; + constexpr TStorageBase(const TStorageBase&) = default; + + ~TStorageBase() = default; + + TStorageBase& operator=(const TStorageBase&) = default; + TStorageBase& operator=(TStorageBase&&) = default; + + union { + char NullState_; + T Data_; + }; + bool Defined_ = false; + }; + + template + struct TStorageBase { + constexpr TStorageBase() noexcept + : NullState_('\0') + { + } + + template + constexpr TStorageBase(TInPlace, Args&&... args) + : Data_(std::forward(args)...) + , Defined_(true) + { + } + + constexpr TStorageBase(TStorageBase&&) = default; + constexpr TStorageBase(const TStorageBase&) = default; + + ~TStorageBase() { + if (this->Defined_) { + this->Data_.~T(); + } + } + + TStorageBase& operator=(const TStorageBase&) = default; + TStorageBase& operator=(TStorageBase&&) = default; + + union { + char NullState_; + T Data_; + }; + bool Defined_ = false; + }; + + // -------------------- COPY CONSTRUCT -------------------- + + template ::value> + struct TCopyBase: TStorageBase { + using TStorageBase::TStorageBase; + }; + + template + struct TCopyBase: TStorageBase { + using TStorageBase::TStorageBase; + + constexpr TCopyBase() = default; + constexpr TCopyBase(const TCopyBase& rhs) { + if (rhs.Defined_) { + new (std::addressof(this->Data_)) T(rhs.Data_); + this->Defined_ = true; + } + } + constexpr TCopyBase(TCopyBase&&) = default; + TCopyBase& operator=(const TCopyBase&) = default; + TCopyBase& operator=(TCopyBase&&) = default; + }; + + // -------------------- MOVE CONSTRUCT -------------------- + + template ::value> + struct TMoveBase: TCopyBase { + using TCopyBase::TCopyBase; + }; + + template + struct TMoveBase: TCopyBase { + using TCopyBase::TCopyBase; + + constexpr TMoveBase() noexcept = default; + constexpr TMoveBase(const TMoveBase&) = default; + constexpr TMoveBase(TMoveBase&& rhs) noexcept(std::is_nothrow_move_constructible::value) { + if (rhs.Defined_) { + new (std::addressof(this->Data_)) T(std::move(rhs.Data_)); + this->Defined_ = true; + } + } + TMoveBase& operator=(const TMoveBase&) = default; + TMoveBase& operator=(TMoveBase&&) = default; + }; + + // -------------------- COPY ASSIGN -------------------- + + template ::value> + struct TCopyAssignBase: TMoveBase { + using TMoveBase::TMoveBase; + }; + + template + struct TCopyAssignBase: TMoveBase { + using TMoveBase::TMoveBase; + + constexpr TCopyAssignBase() noexcept = default; + constexpr TCopyAssignBase(const TCopyAssignBase&) = default; + constexpr TCopyAssignBase(TCopyAssignBase&&) = default; + TCopyAssignBase& operator=(const TCopyAssignBase& rhs) { + if (this->Defined_) { + if (rhs.Defined_) { + this->Data_ = rhs.Data_; + } else { + this->Data_.~T(); + this->Defined_ = false; + } + } else if (rhs.Defined_) { + new (std::addressof(this->Data_)) T(rhs.Data_); + this->Defined_ = true; + } + return *this; + } + TCopyAssignBase& operator=(TCopyAssignBase&&) = default; + }; + + // -------------------- MOVE ASSIGN -------------------- + + template ::value> + struct TMoveAssignBase: TCopyAssignBase { + using TCopyAssignBase::TCopyAssignBase; + }; + + template + struct TMoveAssignBase: TCopyAssignBase { + using TCopyAssignBase::TCopyAssignBase; + + constexpr TMoveAssignBase() noexcept = default; + constexpr TMoveAssignBase(const TMoveAssignBase&) = default; + constexpr TMoveAssignBase(TMoveAssignBase&&) = default; + TMoveAssignBase& operator=(const TMoveAssignBase&) = default; + TMoveAssignBase& operator=(TMoveAssignBase&& rhs) noexcept( + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value) + { + if (this->Defined_) { + if (rhs.Defined_) { + this->Data_ = std::move(rhs.Data_); + } else { + this->Data_.~T(); + this->Defined_ = false; + } + } else if (rhs.Defined_) { + new (std::addressof(this->Data_)) T(std::move(rhs.Data_)); + this->Defined_ = true; + } + return *this; + } + }; +} + +template +using TMaybeBase = NMaybe::TMoveAssignBase; diff --git a/util/generic/maybe_ut.cpp b/util/generic/maybe_ut.cpp new file mode 100644 index 00000000000..84964b8e8b1 --- /dev/null +++ b/util/generic/maybe_ut.cpp @@ -0,0 +1,1126 @@ +#include +#include +#include +#include + +#include "maybe.h" + +class TIncrementOnDestroy { +private: + int* Ptr_; + +public: + TIncrementOnDestroy(int* ptr) noexcept + : Ptr_(ptr) + { + } + + ~TIncrementOnDestroy() { + ++*Ptr_; + } +}; + +Y_UNIT_TEST_SUITE(TMaybeTest) { + Y_UNIT_TEST(TestStatic) { + using T1 = TMaybe; + static_assert(std::is_trivially_copy_constructible::value, ""); + static_assert(std::is_trivially_destructible::value, ""); + + using T2 = TMaybe; + static_assert(std::is_trivially_copy_constructible::value, ""); + static_assert(std::is_trivially_destructible::value, ""); + + using T3 = TMaybe>; + static_assert(std::is_trivially_copy_constructible::value, ""); + static_assert(std::is_trivially_destructible::value, ""); + + using T4 = TMaybe; + static_assert(!std::is_trivially_copy_constructible::value, ""); + static_assert(!std::is_trivially_destructible::value, ""); + } + + Y_UNIT_TEST(TestWarning) { + TMaybe x; + TStringStream ss; + TString line; + + while (ss.ReadLine(line)) { + x = line.size(); + } + + if (x == 5u) { + ss << "5\n"; + } + } + + Y_UNIT_TEST(TTestConstructorDestructor) { + int a = 0; + int b = 0; + + TMaybe(); + UNIT_ASSERT_VALUES_EQUAL(a, b); + + TMaybe(TIncrementOnDestroy(&a)); + b += 2; + UNIT_ASSERT_VALUES_EQUAL(a, b); + + { + TMaybe m1 = TIncrementOnDestroy(&a); + b += 1; + UNIT_ASSERT_VALUES_EQUAL(a, b); + + TMaybe m2 = m1; + UNIT_ASSERT_VALUES_EQUAL(a, b); + + TMaybe m3; + m3 = m1; + UNIT_ASSERT_VALUES_EQUAL(a, b); + } + + b += 3; + UNIT_ASSERT_VALUES_EQUAL(a, b); + + { + TMaybe m4 = TIncrementOnDestroy(&a); + b += 1; + UNIT_ASSERT_VALUES_EQUAL(a, b); + + m4 = TIncrementOnDestroy(&a); + b += 1; + UNIT_ASSERT_VALUES_EQUAL(a, b); + + m4.Clear(); + b += 1; + UNIT_ASSERT_VALUES_EQUAL(a, b); + + m4.Clear(); + UNIT_ASSERT_VALUES_EQUAL(a, b); + } + } + + Y_UNIT_TEST(TestAssignmentClear) { + TMaybe m5; + UNIT_ASSERT(!m5.Defined()); + UNIT_ASSERT(m5.Empty()); + UNIT_ASSERT(m5 == TMaybe()); + UNIT_ASSERT(m5 == Nothing()); + UNIT_ASSERT(m5 != TMaybe(4)); + + m5 = 4; + + UNIT_ASSERT(m5.Defined()); + UNIT_ASSERT(!m5.Empty()); + + UNIT_ASSERT_VALUES_EQUAL(4, m5.GetRef()); + UNIT_ASSERT(m5 == TMaybe(4)); + UNIT_ASSERT(m5 != TMaybe(3)); + UNIT_ASSERT(m5 != TMaybe()); + UNIT_ASSERT(m5 != Nothing()); + + m5 = TMaybe(5); + UNIT_ASSERT(m5.Defined()); + UNIT_ASSERT_VALUES_EQUAL(5, m5.GetRef()); + UNIT_ASSERT(m5 == TMaybe(5)); + UNIT_ASSERT(m5 != TMaybe(4)); + + m5 = TMaybe(); + UNIT_ASSERT(m5.Empty()); + UNIT_ASSERT(m5 == TMaybe()); + UNIT_ASSERT(m5 == Nothing()); + UNIT_ASSERT(m5 != TMaybe(5)); + + m5 = 4; + m5 = Nothing(); + + UNIT_ASSERT(m5.Empty()); + UNIT_ASSERT(m5 == TMaybe()); + UNIT_ASSERT(m5 == Nothing()); + UNIT_ASSERT(m5 != TMaybe(5)); + + m5 = {}; + UNIT_ASSERT(m5.Empty()); + } + + Y_UNIT_TEST(TestInPlace) { + TMaybe m; + + UNIT_ASSERT(!m); + + m.ConstructInPlace(1); + + UNIT_ASSERT(m == 1); + + auto& x = m.ConstructInPlace(2); + + UNIT_ASSERT(m == 2); + x = 7; + UNIT_ASSERT(m == 7); + } + + Y_UNIT_TEST(TestMove) { + struct TMovable { + int Flag = 0; + + TMovable(int flag) + : Flag(flag) + { + } + + TMovable(const TMovable&) = delete; + TMovable& operator=(const TMovable&) = delete; + + TMovable(TMovable&& other) { + std::swap(Flag, other.Flag); + } + TMovable& operator=(TMovable&& other) { + std::swap(Flag, other.Flag); + return *this; + } + }; + + // Move ctor from value + TMovable value1(1); + TMaybe m1(std::move(value1)); + UNIT_ASSERT(m1.Defined()); + UNIT_ASSERT_VALUES_EQUAL(m1->Flag, 1); + + // Move assignment from value + TMovable value2(2); + TMaybe m2; + m2 = std::move(value2); + UNIT_ASSERT(m2.Defined()); + UNIT_ASSERT_VALUES_EQUAL(m2->Flag, 2); + + // Move ctor from maybe + TMaybe m3(std::move(m1)); + UNIT_ASSERT(m3.Defined()); + UNIT_ASSERT_VALUES_EQUAL(m3->Flag, 1); + + // Move assignment from maybe + TMaybe m4; + m4 = std::move(m2); + UNIT_ASSERT(m4.Defined()); + UNIT_ASSERT_VALUES_EQUAL(m4->Flag, 2); + + // Move value from temporary maybe instance + TMovable o5 = *MakeMaybe(5); + UNIT_ASSERT_VALUES_EQUAL(o5.Flag, 5); + TMovable o6 = MakeMaybe(6).GetRef(); + UNIT_ASSERT_VALUES_EQUAL(o6.Flag, 6); + } + + Y_UNIT_TEST(TestCast) { + // Undefined maybe casts to undefined maybe + TMaybe shortMaybe; + const auto undefinedMaybe = shortMaybe.Cast(); + UNIT_ASSERT(!undefinedMaybe.Defined()); + + // Defined maybe casts to defined maybe of another type + shortMaybe = 34; + const auto longMaybe = shortMaybe.Cast(); + UNIT_ASSERT(longMaybe.Defined()); + UNIT_ASSERT_VALUES_EQUAL(34, longMaybe.GetRef()); + } + + Y_UNIT_TEST(TestGetOr) { + UNIT_ASSERT_VALUES_EQUAL(TMaybe().GetOrElse("xxx"), TString("xxx")); + UNIT_ASSERT_VALUES_EQUAL(TMaybe("yyy").GetOrElse("xxx"), TString("yyy")); + + { + TString xxx = "xxx"; + UNIT_ASSERT_VALUES_EQUAL(TMaybe().GetOrElse(xxx).append('x'), TString("xxxx")); + UNIT_ASSERT_VALUES_EQUAL(xxx, "xxxx"); + } + + { + TString xxx = "xxx"; + UNIT_ASSERT_VALUES_EQUAL(TMaybe("yyy").GetOrElse(xxx).append('x'), TString("yyyx")); + UNIT_ASSERT_VALUES_EQUAL(xxx, "xxx"); + } + } + + Y_UNIT_TEST(TestGetOrElseMove) { + struct TData { + int Value = 0; + + TData(int value) + : Value(value) + { + } + + TData(TData&&) = default; + TData& operator=(TData&&) = default; + }; + + TData d5 = MakeMaybe(TData{5}).GetOrElse(TData{6}); + UNIT_ASSERT_VALUES_EQUAL(d5.Value, 5); + TData d6 = TMaybe{}.GetOrElse(TData{6}); + UNIT_ASSERT_VALUES_EQUAL(d6.Value, 6); + + TMaybe d7 = MakeMaybe(TData{7}).OrElse(MakeMaybe(TData{8})); + UNIT_ASSERT_VALUES_EQUAL(d7.GetRef().Value, 7); + TMaybe d8 = TMaybe{}.OrElse(MakeMaybe(TData{8})); + UNIT_ASSERT_VALUES_EQUAL(d8.GetRef().Value, 8); + TMaybe d9 = TMaybe{}.OrElse(TMaybe{}); + UNIT_ASSERT(d9.Empty()); + } + + Y_UNIT_TEST(TestAndThen) { + { + auto f = [](int i) -> TMaybe { + if (i % 2 == 0) { + return i / 2; + } + return {}; + }; + + UNIT_ASSERT_VALUES_EQUAL(TMaybe{4}.AndThen(f), TMaybe{2}); + UNIT_ASSERT_VALUES_EQUAL(TMaybe{5}.AndThen(f), TMaybe{}); + UNIT_ASSERT_VALUES_EQUAL(TMaybe{}.AndThen(f), TMaybe{}); + } + + { + // Move semantics: value stealing + TMaybe x{"Hello, "}; + UNIT_ASSERT_VALUES_EQUAL(std::move(x).AndThen([](TString s) { return MakeMaybe(s + "world!"); }), TMaybe{"Hello, world!"}); + UNIT_ASSERT_VALUES_EQUAL(*x, ""); + } + } + + Y_UNIT_TEST(TestTransform) { + auto f = [](int i) { + return i + 1; + }; + + UNIT_ASSERT_VALUES_EQUAL(TMaybe{1}.Transform(f), TMaybe{2}); + UNIT_ASSERT_VALUES_EQUAL(TMaybe{}.Transform(f), TMaybe{}); + + auto f2 = [](int i) { + return ToString(i); + }; + + UNIT_ASSERT_VALUES_EQUAL(TMaybe{1}.Transform(f2), TMaybe{"1"}); + UNIT_ASSERT_VALUES_EQUAL(TMaybe{}.Transform(f2), TMaybe{}); + + { + // Move semantics: value stealing + TMaybe x{"Hello, "}; + UNIT_ASSERT_VALUES_EQUAL(std::move(x).Transform([](TString s) { return s + "world!"; }), TMaybe("Hello, world!")); + UNIT_ASSERT_VALUES_EQUAL(*x, ""); + } + + { + struct Data { + Data(int x) + : value(x) + { + } + + int value; + int GetValue() { + return value; + } + }; + + UNIT_ASSERT_VALUES_EQUAL(MakeMaybe(5).Transform(std::mem_fn(&Data::value)), TMaybe{5}); + UNIT_ASSERT_VALUES_EQUAL(MakeMaybe(5).Transform(std::mem_fn(&Data::GetValue)), TMaybe{5}); + } + } + + Y_UNIT_TEST(TestOr) { + { + auto f = []() { + return TMaybe{5}; + }; + + UNIT_ASSERT_VALUES_EQUAL(TMaybe{1}.Or(f), TMaybe{1}); + UNIT_ASSERT_VALUES_EQUAL(TMaybe{}.Or(f), TMaybe{5}); + } + + { + bool flag = false; + + auto f = [&flag]() { + flag = true; + return TMaybe{5}; + }; + + TMaybe{5}.Or(f); + UNIT_ASSERT(!flag); + + TMaybe{}.Or(f); + UNIT_ASSERT(flag); + } + + { + TMaybe x{"abacaba"}; + std::move(x).Or([]() { return TMaybe{}; }); + UNIT_ASSERT_VALUES_EQUAL(*x, ""); + } + } + + /* + == + != + < + <= + > + >= +*/ + + Y_UNIT_TEST(TestCompareEqualEmpty) { + TMaybe m1; + TMaybe m2; + + UNIT_ASSERT(m1 == m2); + UNIT_ASSERT(!(m1 != m2)); + UNIT_ASSERT(!(m1 < m2)); + UNIT_ASSERT(m1 <= m2); + UNIT_ASSERT(!(m1 > m2)); + UNIT_ASSERT(m1 >= m2); + } + + Y_UNIT_TEST(TestCompareEqualNonEmpty) { + TMaybe m1{1}; + TMaybe m2{1}; + + UNIT_ASSERT(m1 == m2); + UNIT_ASSERT(!(m1 != m2)); + UNIT_ASSERT(!(m1 < m2)); + UNIT_ASSERT(m1 <= m2); + UNIT_ASSERT(!(m1 > m2)); + UNIT_ASSERT(m1 >= m2); + } + + Y_UNIT_TEST(TestCompareOneLessThanOther) { + TMaybe m1{1}; + TMaybe m2{2}; + + UNIT_ASSERT(!(m1 == m2)); + UNIT_ASSERT(m1 != m2); + UNIT_ASSERT(m1 < m2); + UNIT_ASSERT(m1 <= m2); + UNIT_ASSERT(!(m1 > m2)); + UNIT_ASSERT(!(m1 >= m2)); + } + + Y_UNIT_TEST(TestCompareTMaybeAndT_Equal) { + TMaybe m{1}; + int v{1}; + + UNIT_ASSERT(m == v); + UNIT_ASSERT(!(m != v)); + UNIT_ASSERT(!(m < v)); + UNIT_ASSERT(m <= v); + UNIT_ASSERT(!(m > v)); + UNIT_ASSERT(m >= v); + + UNIT_ASSERT(v == m); + UNIT_ASSERT(!(v != m)); + UNIT_ASSERT(!(v < m)); + UNIT_ASSERT(v <= m); + UNIT_ASSERT(!(v > m)); + UNIT_ASSERT(v >= m); + } + + Y_UNIT_TEST(TestCompareTMaybeAndT_TMaybeLessThanT) { + TMaybe m{1}; + int v{2}; + + UNIT_ASSERT(!(m == v)); + UNIT_ASSERT(m != v); + UNIT_ASSERT(m < v); + UNIT_ASSERT(m <= v); + UNIT_ASSERT(!(m > v)); + UNIT_ASSERT(!(m >= v)); + + UNIT_ASSERT(!(v == m)); + UNIT_ASSERT(v != m); + UNIT_ASSERT(!(v < m)); + UNIT_ASSERT(!(v <= m)); + UNIT_ASSERT(v > m); + UNIT_ASSERT(v >= m); + } + + Y_UNIT_TEST(TestCompareTMaybeAndT_TMaybeGreaterThanT) { + TMaybe m{2}; + int v{1}; + + UNIT_ASSERT(!(m == v)); + UNIT_ASSERT(m != v); + UNIT_ASSERT(!(m < v)); + UNIT_ASSERT(!(m <= v)); + UNIT_ASSERT(m > v); + UNIT_ASSERT(m >= v); + + UNIT_ASSERT(!(v == m)); + UNIT_ASSERT(v != m); + UNIT_ASSERT(v < m); + UNIT_ASSERT(v <= m); + UNIT_ASSERT(!(v > m)); + UNIT_ASSERT(!(v >= m)); + } + + Y_UNIT_TEST(TestCompareEmptyTMaybeAndT) { + TMaybe m; + int v{1}; + + UNIT_ASSERT(!(m == v)); + UNIT_ASSERT(m != v); + UNIT_ASSERT(m < v); + UNIT_ASSERT(m <= v); + UNIT_ASSERT(!(m > v)); + UNIT_ASSERT(!(m >= v)); + + UNIT_ASSERT(!(v == m)); + UNIT_ASSERT(v != m); + UNIT_ASSERT(!(v < m)); + UNIT_ASSERT(!(v <= m)); + UNIT_ASSERT(v > m); + UNIT_ASSERT(v >= m); + } + + Y_UNIT_TEST(TestCompareEmptyTMaybeAndNothing) { + TMaybe m; + auto n = Nothing(); + + UNIT_ASSERT(m == n); + UNIT_ASSERT(!(m != n)); + UNIT_ASSERT(!(m < n)); + UNIT_ASSERT(m <= n); + UNIT_ASSERT(!(m > n)); + UNIT_ASSERT(m >= n); + + UNIT_ASSERT(n == m); + UNIT_ASSERT(!(n != m)); + UNIT_ASSERT(!(n < m)); + UNIT_ASSERT(n <= m); + UNIT_ASSERT(!(n > m)); + UNIT_ASSERT(n >= m); + } + + Y_UNIT_TEST(TestCompareNonEmptyTMaybeAndNothing) { + TMaybe m{1}; + auto n = Nothing(); + + UNIT_ASSERT(!(m == n)); + UNIT_ASSERT(m != n); + UNIT_ASSERT(!(m < n)); + UNIT_ASSERT(!(m <= n)); + UNIT_ASSERT(m > n); + UNIT_ASSERT(m >= n); + + UNIT_ASSERT(!(n == m)); + UNIT_ASSERT(n != m); + UNIT_ASSERT(n < m); + UNIT_ASSERT(n <= m); + UNIT_ASSERT(!(n > m)); + UNIT_ASSERT(!(n >= m)); + } + + Y_UNIT_TEST(TestCompareTMaybeAndConvertibleT_Equal) { + TMaybe m{1}; + unsigned int v{1}; + + UNIT_ASSERT(m == v); + UNIT_ASSERT(!(m != v)); + UNIT_ASSERT(!(m < v)); + UNIT_ASSERT(m <= v); + UNIT_ASSERT(!(m > v)); + UNIT_ASSERT(m >= v); + + UNIT_ASSERT(v == m); + UNIT_ASSERT(!(v != m)); + UNIT_ASSERT(!(v < m)); + UNIT_ASSERT(v <= m); + UNIT_ASSERT(!(v > m)); + UNIT_ASSERT(v >= m); + } + + Y_UNIT_TEST(TestCompareTMaybeAndConvertibleT_TMaybeLessThanT) { + TMaybe m{1}; + unsigned int v{2}; + + UNIT_ASSERT(!(m == v)); + UNIT_ASSERT(m != v); + UNIT_ASSERT(m < v); + UNIT_ASSERT(m <= v); + UNIT_ASSERT(!(m > v)); + UNIT_ASSERT(!(m >= v)); + + UNIT_ASSERT(!(v == m)); + UNIT_ASSERT(v != m); + UNIT_ASSERT(!(v < m)); + UNIT_ASSERT(!(v <= m)); + UNIT_ASSERT(v > m); + UNIT_ASSERT(v >= m); + } + + Y_UNIT_TEST(TestCompareTMaybeAndConvertibleT_TMaybeGreaterThanT) { + TMaybe m{2}; + unsigned int v{1}; + + UNIT_ASSERT(!(m == v)); + UNIT_ASSERT(m != v); + UNIT_ASSERT(!(m < v)); + UNIT_ASSERT(!(m <= v)); + UNIT_ASSERT(m > v); + UNIT_ASSERT(m >= v); + + UNIT_ASSERT(!(v == m)); + UNIT_ASSERT(v != m); + UNIT_ASSERT(v < m); + UNIT_ASSERT(v <= m); + UNIT_ASSERT(!(v > m)); + UNIT_ASSERT(!(v >= m)); + } + + Y_UNIT_TEST(TestCompareEmptyTMaybeAndConvertibleT) { + TMaybe m; + unsigned int v{1}; + + UNIT_ASSERT(!(m == v)); + UNIT_ASSERT(m != v); + UNIT_ASSERT(m < v); + UNIT_ASSERT(m <= v); + UNIT_ASSERT(!(m > v)); + UNIT_ASSERT(!(m >= v)); + + UNIT_ASSERT(!(v == m)); + UNIT_ASSERT(v != m); + UNIT_ASSERT(!(v < m)); + UNIT_ASSERT(!(v <= m)); + UNIT_ASSERT(v > m); + UNIT_ASSERT(v >= m); + } + + Y_UNIT_TEST(TestMakeMaybe) { + { + auto m1 = MakeMaybe(1); + UNIT_ASSERT(*m1 == 1); + } + + { + struct TMockClass { + TMockClass(int i) + : I_(i) + { + } + + TMockClass(const TMockClass& other) + : I_(other.I_) + { + IsCopyConstructorCalled_ = true; + } + + TMockClass& operator=(const TMockClass& other) { + if (this != &other) { + I_ = other.I_; + IsCopyAssignmentOperatorCalled_ = true; + } + + return *this; + } + + TMockClass(TMockClass&& other) + : I_(other.I_) + { + IsMoveConstructorCalled_ = true; + } + + TMockClass& operator=(TMockClass&& other) { + if (this != &other) { + I_ = other.I_; + IsMoveAssignmentOperatorCalled_ = true; + } + + return *this; + } + + int I_; + bool IsCopyConstructorCalled_{false}; + bool IsMoveConstructorCalled_{false}; + bool IsCopyAssignmentOperatorCalled_{false}; + bool IsMoveAssignmentOperatorCalled_{false}; + }; + + auto m2 = MakeMaybe(1); + UNIT_ASSERT(m2->I_ == 1); + UNIT_ASSERT(!m2->IsCopyConstructorCalled_); + UNIT_ASSERT(!m2->IsMoveConstructorCalled_); + UNIT_ASSERT(!m2->IsCopyAssignmentOperatorCalled_); + UNIT_ASSERT(!m2->IsMoveAssignmentOperatorCalled_); + } + + { + auto m3 = MakeMaybe>({1, 2, 3, 4, 5}); + UNIT_ASSERT(m3->size() == 5); + UNIT_ASSERT(m3->at(0) == 1); + UNIT_ASSERT(m3->at(1) == 2); + UNIT_ASSERT(m3->at(2) == 3); + UNIT_ASSERT(m3->at(3) == 4); + UNIT_ASSERT(m3->at(4) == 5); + } + + { + struct TMockStruct4 { + TMockStruct4(int a, int b, int c) + : A_(a) + , B_(b) + , C_(c) + { + } + + int A_; + int B_; + int C_; + }; + + auto m4 = MakeMaybe(1, 2, 3); + UNIT_ASSERT(m4->A_ == 1); + UNIT_ASSERT(m4->B_ == 2); + UNIT_ASSERT(m4->C_ == 3); + } + + { + struct TMockStruct5 { + TMockStruct5(const TVector& vec, bool someFlag) + : Vec_(vec) + , SomeFlag_(someFlag) + { + } + + TVector Vec_; + bool SomeFlag_; + }; + + auto m5 = MakeMaybe({1, 2, 3}, true); + UNIT_ASSERT(m5->Vec_.size() == 3); + UNIT_ASSERT(m5->Vec_[0] == 1); + UNIT_ASSERT(m5->Vec_[1] == 2); + UNIT_ASSERT(m5->Vec_[2] == 3); + UNIT_ASSERT(m5->SomeFlag_); + } + } + + Y_UNIT_TEST(TestSwappingUsingMemberSwap) { + { + TMaybe m1 = 1; + TMaybe m2 = 2; + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(*m2 == 2); + + m1.Swap(m2); + + UNIT_ASSERT(*m1 == 2); + UNIT_ASSERT(*m2 == 1); + } + + { + TMaybe m1 = 1; + TMaybe m2 = Nothing(); + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(m2 == Nothing()); + + m1.Swap(m2); + + UNIT_ASSERT(m1 == Nothing()); + UNIT_ASSERT(*m2 == 1); + } + + { + TMaybe m1 = Nothing(); + TMaybe m2 = 1; + + UNIT_ASSERT(m1 == Nothing()); + UNIT_ASSERT(*m2 == 1); + + m1.Swap(m2); + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(m2 == Nothing()); + } + } + + Y_UNIT_TEST(TestSwappingUsingMemberLittleSwap) { + { + TMaybe m1 = 1; + TMaybe m2 = 2; + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(*m2 == 2); + + m1.swap(m2); + + UNIT_ASSERT(*m1 == 2); + UNIT_ASSERT(*m2 == 1); + } + + { + TMaybe m1 = 1; + TMaybe m2 = Nothing(); + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(m2 == Nothing()); + + m1.swap(m2); + + UNIT_ASSERT(m1 == Nothing()); + UNIT_ASSERT(*m2 == 1); + } + + { + TMaybe m1 = Nothing(); + TMaybe m2 = 1; + + UNIT_ASSERT(m1 == Nothing()); + UNIT_ASSERT(*m2 == 1); + + m1.swap(m2); + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(m2 == Nothing()); + } + } + + Y_UNIT_TEST(TestSwappingUsingGlobalSwap) { + { + TMaybe m1 = 1; + TMaybe m2 = 2; + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(*m2 == 2); + + ::Swap(m1, m2); + + UNIT_ASSERT(*m1 == 2); + UNIT_ASSERT(*m2 == 1); + } + + { + TMaybe m1 = 1; + TMaybe m2 = Nothing(); + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(m2 == Nothing()); + + ::Swap(m1, m2); + + UNIT_ASSERT(m1 == Nothing()); + UNIT_ASSERT(*m2 == 1); + } + + { + TMaybe m1 = Nothing(); + TMaybe m2 = 1; + + UNIT_ASSERT(m1 == Nothing()); + UNIT_ASSERT(*m2 == 1); + + ::Swap(m1, m2); + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(m2 == Nothing()); + } + } + + Y_UNIT_TEST(TestSwappingUsingGlobalDoSwap) { + { + TMaybe m1 = 1; + TMaybe m2 = 2; + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(*m2 == 2); + + ::DoSwap(m1, m2); + + UNIT_ASSERT(*m1 == 2); + UNIT_ASSERT(*m2 == 1); + } + + { + TMaybe m1 = 1; + TMaybe m2 = Nothing(); + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(m2 == Nothing()); + + ::DoSwap(m1, m2); + + UNIT_ASSERT(m1 == Nothing()); + UNIT_ASSERT(*m2 == 1); + } + + { + TMaybe m1 = Nothing(); + TMaybe m2 = 1; + + UNIT_ASSERT(m1 == Nothing()); + UNIT_ASSERT(*m2 == 1); + + ::DoSwap(m1, m2); + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(m2 == Nothing()); + } + } + + Y_UNIT_TEST(TestSwappingUsingStdSwap) { + { + TMaybe m1 = 1; + TMaybe m2 = 2; + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(*m2 == 2); + + ::std::swap(m1, m2); + + UNIT_ASSERT(*m1 == 2); + UNIT_ASSERT(*m2 == 1); + } + + { + TMaybe m1 = 1; + TMaybe m2 = Nothing(); + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(m2 == Nothing()); + + ::std::swap(m1, m2); + + UNIT_ASSERT(m1 == Nothing()); + UNIT_ASSERT(*m2 == 1); + } + + { + TMaybe m1 = Nothing(); + TMaybe m2 = 1; + + UNIT_ASSERT(m1 == Nothing()); + UNIT_ASSERT(*m2 == 1); + + ::std::swap(m1, m2); + + UNIT_ASSERT(*m1 == 1); + UNIT_ASSERT(m2 == Nothing()); + } + } + + Y_UNIT_TEST(TestOutputStreamEmptyMaybe) { + TString s; + TStringOutput output(s); + output << TMaybe(); + UNIT_ASSERT_EQUAL("(empty maybe)", s); + } + + Y_UNIT_TEST(TestOutputStreamNothing) { + TString s; + TStringOutput output(s); + output << Nothing(); + UNIT_ASSERT_VALUES_EQUAL("(empty maybe)", s); + } + + Y_UNIT_TEST(TestOutputStreamDefinedMaybe) { + TString s; + TStringOutput output(s); + output << TMaybe(42); + UNIT_ASSERT_EQUAL("42", s); + } + + Y_UNIT_TEST(TestMaybeCovarianceImplicit) { + struct TestStruct { + TestStruct(int value) + : Value_(value) + { + } + + operator int() const { + return Value_; + } + + static TMaybe Unwrap(TMaybe testStructMaybe) { + return testStructMaybe; + } + + int Value_; + }; + + TMaybe testMaybeFull = TestStruct::Unwrap(TMaybe(42)); + UNIT_ASSERT(testMaybeFull.Defined()); + UNIT_ASSERT_EQUAL(testMaybeFull.GetRef(), 42); + + TMaybe testMaybeEmpty = TestStruct::Unwrap(TMaybe()); + UNIT_ASSERT(!testMaybeEmpty.Defined()); + } + + Y_UNIT_TEST(TestMaybeCovarianceExplicit) { + struct TestStruct { + explicit TestStruct(int value) + : Value_(value) + { + } + int Value_; + }; + + TMaybe testStructMaybeFull(TMaybe(42)); + UNIT_ASSERT(testStructMaybeFull.Defined()); + UNIT_ASSERT_EQUAL(testStructMaybeFull.GetRef().Value_, 42); + + TMaybe empty; + TMaybe testStructMaybeEmpty(empty); + UNIT_ASSERT(!testStructMaybeEmpty.Defined()); + } + + Y_UNIT_TEST(TestMaybeCovarianceAssign) { + struct TestStruct { + explicit TestStruct(int value) + : Value_(value) + { + } + TestStruct& operator=(int value) { + Value_ = value; + return *this; + } + int Value_; + }; + + TMaybe testStructMaybe(Nothing()); + UNIT_ASSERT(!testStructMaybe.Defined()); + + testStructMaybe = TMaybe(42); + UNIT_ASSERT(testStructMaybe.Defined()); + UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().Value_, 42); + + testStructMaybe = TMaybe(23); + UNIT_ASSERT(testStructMaybe.Defined()); + UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().Value_, 23); + + testStructMaybe = TMaybe(); + UNIT_ASSERT(!testStructMaybe.Defined()); + } + + Y_UNIT_TEST(TestMaybeCovarianceNonTrivial) { + struct TestStruct { + enum { + FromValue, + FromMaybe, + }; + TestStruct(int value) + : Value_(value) + , From_(FromValue) + { + } + TestStruct(TMaybe value) + : Value_(value.Defined() ? value.GetRef() : 0) + , From_(FromMaybe) + { + } + int Value_; + int From_; + }; + + TMaybe testStructFromMaybe(TMaybe(42)); + UNIT_ASSERT(testStructFromMaybe.Defined()); + UNIT_ASSERT_EQUAL(testStructFromMaybe.GetRef().From_, TestStruct::FromMaybe); + UNIT_ASSERT_EQUAL(testStructFromMaybe.GetRef().Value_, 42); + + TMaybe empty; + TMaybe testStructFromEmptyMaybe(empty); + UNIT_ASSERT(testStructFromEmptyMaybe.Defined()); + UNIT_ASSERT_EQUAL(testStructFromEmptyMaybe.GetRef().From_, TestStruct::FromMaybe); + UNIT_ASSERT_EQUAL(testStructFromEmptyMaybe.GetRef().Value_, 0); + + TMaybe testStructFromValue(23); + UNIT_ASSERT(testStructFromValue.Defined()); + UNIT_ASSERT_EQUAL(testStructFromValue.GetRef().From_, TestStruct::FromValue); + UNIT_ASSERT_EQUAL(testStructFromValue.GetRef().Value_, 23); + } + + Y_UNIT_TEST(TestMaybeCovarianceNonTrivialAssign) { + struct TestStruct { + enum { + FromValue, + FromMaybe, + }; + TestStruct(int value) + : Value_(value) + , From_(FromValue) + { + } + TestStruct(TMaybe value) + : Value_(value.Defined() ? value.GetRef() : 0) + , From_(FromMaybe) + { + } + TestStruct& operator=(int value) { + Value_ = value; + From_ = FromValue; + return *this; + } + TestStruct& operator=(TMaybe value) { + Value_ = value.Defined() ? value.GetRef() : 0; + From_ = FromMaybe; + return *this; + } + int Value_; + int From_; + }; + + TMaybe testStructMaybe(Nothing()); + testStructMaybe = TMaybe(42); + UNIT_ASSERT(testStructMaybe.Defined()); + UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().From_, TestStruct::FromMaybe); + UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().Value_, 42); + + testStructMaybe = TMaybe(); + UNIT_ASSERT(testStructMaybe.Defined()); + UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().From_, TestStruct::FromMaybe); + UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().Value_, 0); + + testStructMaybe = 23; + UNIT_ASSERT(testStructMaybe.Defined()); + UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().From_, TestStruct::FromValue); + UNIT_ASSERT_EQUAL(testStructMaybe.GetRef().Value_, 23); + } + + Y_UNIT_TEST(TestMaybeConvertion) { + struct TSrc {}; + struct TDst { + bool FromMaybeConstructorApplied; + + explicit TDst(TSrc) + : FromMaybeConstructorApplied(false) + { + } + + explicit TDst(TMaybe) + : FromMaybeConstructorApplied(true) + { + } + + TDst& operator=(TSrc) { + FromMaybeConstructorApplied = false; + return *this; + } + TDst& operator=(TMaybe) { + FromMaybeConstructorApplied = true; + return *this; + } + }; + + auto m = TMaybe(TMaybe()); + UNIT_ASSERT(m.Defined()); + UNIT_ASSERT(m->FromMaybeConstructorApplied); + + m = TMaybe(); + UNIT_ASSERT(m.Defined()); + UNIT_ASSERT(m->FromMaybeConstructorApplied); + } + + Y_UNIT_TEST(TestOnEmptyException) { + TMaybe v; + UNIT_ASSERT_EXCEPTION_CONTAINS(v.GetRef(), yexception, "StringBuf"); + } +} diff --git a/util/generic/maybe_ut.pyx b/util/generic/maybe_ut.pyx new file mode 100644 index 00000000000..c514d9b62c0 --- /dev/null +++ b/util/generic/maybe_ut.pyx @@ -0,0 +1,185 @@ +from util.generic.maybe cimport TMaybe, Nothing + +import pytest +import unittest + + +def _check_from_py(TMaybe[int] x): + return x.Defined() + + +def _check_to_py_value(): + cdef TMaybe[int] tmp = TMaybe[int](42) + return tmp + + +def _check_to_py_nothing(): + cdef TMaybe[int] tmp = Nothing() + return tmp + + +class TestMaybe(unittest.TestCase): + + def test_ctor1(self): + cdef TMaybe[int] tmp = TMaybe[int]() + self.assertFalse(tmp.Defined()) + + def test_ctor2(self): + cdef TMaybe[int] tmp = TMaybe[int](42) + self.assertTrue(tmp.Defined()) + self.assertEqual(tmp.GetRef(), 42) + + def test_ctor3(self): + cdef TMaybe[int] tmp = Nothing() + self.assertFalse(tmp.Defined()) + + def test_operator_assign(self): + cdef TMaybe[int] tmp + tmp = 42 + self.assertTrue(tmp.Defined()) + self.assertEqual(tmp.GetRef(), 42) + + def test_compare(self): + cdef TMaybe[int] tmp1 = 17 + cdef TMaybe[int] tmp2 = 42 + cdef TMaybe[int] nothing + + # == + self.assertTrue(tmp1 == 17) + self.assertTrue(tmp1 == tmp1) + self.assertTrue(nothing == nothing) + + self.assertFalse(tmp1 == 16) + self.assertFalse(tmp1 == tmp2) + self.assertFalse(tmp1 == nothing) + + # != + self.assertTrue(tmp1 != 16) + self.assertTrue(tmp1 != tmp2) + self.assertTrue(tmp1 != nothing) + + self.assertFalse(tmp1 != 17) + self.assertFalse(tmp1 != tmp1) + self.assertFalse(nothing != nothing) + + # < + self.assertTrue(nothing < tmp1) + self.assertTrue(nothing < tmp2) + self.assertTrue(tmp1 < tmp2) + self.assertTrue(nothing < 0) + self.assertTrue(tmp1 < 18) + + self.assertFalse(nothing < nothing) + self.assertFalse(tmp1 < tmp1) + self.assertFalse(tmp2 < tmp1) + self.assertFalse(tmp1 < 16) + + # <= + self.assertTrue(nothing <= nothing) + self.assertTrue(nothing <= tmp1) + self.assertTrue(nothing <= tmp2) + self.assertTrue(tmp1 <= tmp1) + self.assertTrue(tmp1 <= tmp2) + self.assertTrue(nothing <= 0) + self.assertTrue(tmp1 <= 18) + + self.assertFalse(tmp2 <= tmp1) + self.assertFalse(tmp1 <= 16) + + # > + self.assertTrue(tmp1 > nothing) + self.assertTrue(tmp2 > nothing) + self.assertTrue(tmp2 > tmp1) + self.assertTrue(tmp1 > 16) + + self.assertFalse(nothing > nothing) + self.assertFalse(nothing > 0) + self.assertFalse(tmp1 > tmp1) + self.assertFalse(tmp1 > tmp2) + self.assertFalse(tmp1 > 18) + + # >= + self.assertTrue(nothing >= nothing) + self.assertTrue(tmp1 >= nothing) + self.assertTrue(tmp2 >= nothing) + self.assertTrue(tmp2 >= tmp1) + self.assertTrue(tmp1 >= tmp1) + self.assertTrue(tmp1 >= 16) + + self.assertFalse(nothing >= 0) + self.assertFalse(tmp1 >= tmp2) + self.assertFalse(tmp1 >= 18) + + def test_construct_in_place(self): + cdef TMaybe[int] tmp + tmp.ConstructInPlace(42) + self.assertTrue(tmp.Defined()) + self.assertEqual(tmp.GetRef(), 42) + + def test_clear(self): + cdef TMaybe[int] tmp = 42 + tmp.Clear() + self.assertFalse(tmp.Defined()) + + def test_defined(self): + cdef TMaybe[int] tmp + self.assertFalse(tmp.Defined()) + self.assertTrue(tmp.Empty()) + tmp = 42 + self.assertTrue(tmp.Defined()) + self.assertFalse(tmp.Empty()) + + def test_check_defined(self): + cdef TMaybe[int] tmp + with pytest.raises(RuntimeError): + tmp.CheckDefined() + tmp = 42 + tmp.CheckDefined() + + def test_get(self): + cdef TMaybe[int] tmp = 42 + cdef int* p = tmp.Get() + self.assertTrue(p != NULL) + self.assertEqual(p[0], 42) + + def test_get_ref(self): + cdef TMaybe[int] tmp = 42 + self.assertTrue(tmp.Defined()) + self.assertEqual(tmp.GetRef(), 42) + + def test_get_or_else(self): + cdef TMaybe[int] tmp = 42 + self.assertEqual(tmp.GetOrElse(13), 42) + tmp.Clear() + self.assertEqual(tmp.GetOrElse(13), 13) + + def test_or_else(self): + cdef TMaybe[int] tmp = 42 + cdef TMaybe[int] nothing + self.assertFalse(nothing.OrElse(nothing).Defined()) + self.assertEqual(tmp.OrElse(nothing).GetRef(), 42) + self.assertEqual(nothing.OrElse(tmp).GetRef(), 42) + self.assertEqual(tmp.OrElse(tmp).GetRef(), 42) + + def test_cast(self): + cdef TMaybe[int] tmp = 42 + cdef TMaybe[char] tmp2 = tmp.Cast[char]() + self.assertEqual(tmp2.GetRef(), 42) + + def test_swap(self): + cdef TMaybe[int] tmp1 = 42 + cdef TMaybe[int] tmp2 + tmp2.Swap(tmp1) + self.assertFalse(tmp1.Defined()) + self.assertEqual(tmp2.GetRef(), 42) + + def test_from_py(self): + self.assertTrue(_check_from_py(42)) + self.assertFalse(_check_from_py(None)) + + with self.assertRaises(TypeError): + _check_from_py("ttt") + + def test_to_py(self): + self.assertEqual(_check_to_py_value(), 42) + self.assertEqual(_check_to_py_nothing(), None) diff --git a/util/generic/mem_copy.cpp b/util/generic/mem_copy.cpp new file mode 100644 index 00000000000..dd96c6fbcf5 --- /dev/null +++ b/util/generic/mem_copy.cpp @@ -0,0 +1 @@ +#include "mem_copy.h" diff --git a/util/generic/mem_copy.h b/util/generic/mem_copy.h new file mode 100644 index 00000000000..b68c852953a --- /dev/null +++ b/util/generic/mem_copy.h @@ -0,0 +1,55 @@ +#pragma once + +#include "typetraits.h" + +#include + +#include + +template +using TIfPOD = std::enable_if_t::IsPod, T*>; + +template +using TIfNotPOD = std::enable_if_t::IsPod, T*>; + +template +static inline TIfPOD MemCopy(T* to, const T* from, size_t n) noexcept { + if (n) { + memcpy(to, from, n * sizeof(T)); + } + + return to; +} + +template +static inline TIfNotPOD MemCopy(T* to, const T* from, size_t n) { + for (size_t i = 0; i < n; ++i) { + to[i] = from[i]; + } + + return to; +} + +template +static inline TIfPOD MemMove(T* to, const T* from, size_t n) noexcept { + if (n) { + memmove(to, from, n * sizeof(T)); + } + + return to; +} + +template +static inline TIfNotPOD MemMove(T* to, const T* from, size_t n) { + if (to <= from || to >= from + n) { + return MemCopy(to, from, n); + } + + //copy backwards + while (n) { + to[n - 1] = from[n - 1]; + --n; + } + + return to; +} diff --git a/util/generic/mem_copy_ut.cpp b/util/generic/mem_copy_ut.cpp new file mode 100644 index 00000000000..8b55a11cf68 --- /dev/null +++ b/util/generic/mem_copy_ut.cpp @@ -0,0 +1,113 @@ +#include "mem_copy.h" + +#include + +namespace { + class TAssignBCalled: public yexception { + }; + + struct TB { + inline TB& operator=(const TB&) { + throw TAssignBCalled(); + + return *this; + } + }; + + struct TC: public TB { + }; +} + +Y_DECLARE_PODTYPE(TB); + +Y_UNIT_TEST_SUITE(TestMemCopy) { + Y_UNIT_TEST(Test1) { + char buf[] = "123"; + char buf1[sizeof(buf)]; + + UNIT_ASSERT_EQUAL(MemCopy(buf1, buf, sizeof(buf)), buf1); + + for (size_t i = 0; i < sizeof(buf); ++i) { + UNIT_ASSERT_VALUES_EQUAL(buf[i], buf1[i]); + } + } + + static int x = 0; + + struct TA { + inline TA() { + X = ++x; + } + + int X; + }; + + Y_UNIT_TEST(Test2) { + x = 0; + + TA a1[5]; + TA a2[5]; + + UNIT_ASSERT_VALUES_EQUAL(a1[0].X, 1); + UNIT_ASSERT_VALUES_EQUAL(a2[0].X, 6); + + MemCopy(a2, a1, 5); + + for (size_t i = 0; i < 5; ++i) { + UNIT_ASSERT_VALUES_EQUAL(a1[i].X, a2[i].X); + } + } + + Y_UNIT_TEST(Test3) { + TB b1[5]; + TB b2[5]; + + MemCopy(b2, b1, 5); + } + + Y_UNIT_TEST(Test4) { + TC c1[5]; + TC c2[5]; + + UNIT_ASSERT_EXCEPTION(MemCopy(c2, c1, 5), TAssignBCalled); + } + + template + inline void FillX(T* b, T* e) { + int tmp = 0; + + while (b != e) { + (b++)->X = ++tmp; + } + } + + Y_UNIT_TEST(Test5) { + struct TD { + int X; + }; + + TD orig[50]; + + for (ssize_t i = -15; i < 15; ++i) { + TD* b = orig + 20; + TD* e = b + 10; + + FillX(b, e); + + TD* to = b + i; + + MemMove(to, b, e - b - 1); + + for (size_t j = 0; j < (e - b) - (size_t)1; ++j) { + UNIT_ASSERT_VALUES_EQUAL(to[j].X, j + 1); + } + } + } + + Y_UNIT_TEST(TestEmpty) { + char* tmp = nullptr; + + UNIT_ASSERT(MemCopy(tmp, tmp, 0) == nullptr); + UNIT_ASSERT(MemMove(tmp, tmp, 0) == nullptr); + } +} diff --git a/util/generic/noncopyable.cpp b/util/generic/noncopyable.cpp new file mode 100644 index 00000000000..a614dca34fa --- /dev/null +++ b/util/generic/noncopyable.cpp @@ -0,0 +1 @@ +#include "noncopyable.h" diff --git a/util/generic/noncopyable.h b/util/generic/noncopyable.h new file mode 100644 index 00000000000..c007934133b --- /dev/null +++ b/util/generic/noncopyable.h @@ -0,0 +1,38 @@ +#pragma once + +/** + * @class TNonCopyable + * + * Inherit your class from `TNonCopyable` if you want to make it noncopyable. + * + * Example usage: + * @code + * class Foo: private TNonCopyable { + * // ... + * }; + * @endcode + */ + +namespace NNonCopyable { // protection from unintended ADL + struct TNonCopyable { + TNonCopyable(const TNonCopyable&) = delete; + TNonCopyable& operator=(const TNonCopyable&) = delete; + + TNonCopyable() = default; + ~TNonCopyable() = default; + }; + + struct TMoveOnly { + TMoveOnly(TMoveOnly&&) noexcept = default; + TMoveOnly& operator=(TMoveOnly&&) noexcept = default; + + TMoveOnly(const TMoveOnly&) = delete; + TMoveOnly& operator=(const TMoveOnly&) = delete; + + TMoveOnly() = default; + ~TMoveOnly() = default; + }; +} + +using TNonCopyable = NNonCopyable::TNonCopyable; +using TMoveOnly = NNonCopyable::TMoveOnly; diff --git a/util/generic/object_counter.cpp b/util/generic/object_counter.cpp new file mode 100644 index 00000000000..09f4b272b56 --- /dev/null +++ b/util/generic/object_counter.cpp @@ -0,0 +1 @@ +#include "object_counter.h" diff --git a/util/generic/object_counter.h b/util/generic/object_counter.h new file mode 100644 index 00000000000..57617101eea --- /dev/null +++ b/util/generic/object_counter.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +/** + * Simple thread-safe per-class counter that can be used to make sure you don't + * have any leaks in your code, or for statistical purposes. + * + * Example usage: + * \code + * class TMyClass: public TObjectCounter { + * // ... + * }; + * + * // In your code: + * Cerr << "TMyClass instances in use: " << TMyClass::ObjectCount() << Endl; + * \endcode + */ +template +class TObjectCounter { +public: + inline TObjectCounter() noexcept { + ++Count_; + } + + inline TObjectCounter(const TObjectCounter& /*item*/) noexcept { + ++Count_; + } + + inline ~TObjectCounter() { + --Count_; + } + + static inline long ObjectCount() noexcept { + return Count_.load(); + } + + /** + * Resets object count. Mainly for tests, as you don't want to do this in + * your code and then end up with negative counts. + * + * \returns Current object count. + */ + static inline long ResetObjectCount() noexcept { + return Count_.exchange(0); + } + +private: + static std::atomic Count_; +}; + +template +std::atomic TObjectCounter::Count_ = 0; diff --git a/util/generic/objects_counter_ut.cpp b/util/generic/objects_counter_ut.cpp new file mode 100644 index 00000000000..4d5da37a56f --- /dev/null +++ b/util/generic/objects_counter_ut.cpp @@ -0,0 +1,36 @@ +#include "object_counter.h" + +#include + +Y_UNIT_TEST_SUITE(ObjectsCounter) { + struct TObject: public TObjectCounter { + }; + + Y_UNIT_TEST(Test1) { + TObject obj; + TVector objects; + for (ui32 i = 0; i < 100; ++i) { + objects.push_back(obj); + } + UNIT_ASSERT_EQUAL(TObjectCounter::ObjectCount(), 101); + } + + Y_UNIT_TEST(TestEq) { + TObject obj; + { + TObject obj1 = obj; + UNIT_ASSERT_EQUAL(TObjectCounter::ObjectCount(), 2); + } + UNIT_ASSERT_EQUAL(TObjectCounter::ObjectCount(), 1); + } + + Y_UNIT_TEST(TestMove) { + TObject obj; + UNIT_ASSERT_EQUAL(TObjectCounter::ObjectCount(), 1); + { + TObject obj1 = std::move(obj); + UNIT_ASSERT_EQUAL(TObjectCounter::ObjectCount(), 2); + } + UNIT_ASSERT_EQUAL(TObjectCounter::ObjectCount(), 1); + } +} diff --git a/util/generic/overloaded.cpp b/util/generic/overloaded.cpp new file mode 100644 index 00000000000..fb6ff88d31c --- /dev/null +++ b/util/generic/overloaded.cpp @@ -0,0 +1 @@ +#include "overloaded.h" diff --git a/util/generic/overloaded.h b/util/generic/overloaded.h new file mode 100644 index 00000000000..96a97e44bc8 --- /dev/null +++ b/util/generic/overloaded.h @@ -0,0 +1,56 @@ +#pragma once + +/** + * Construct an ad-hoc object with an overloaded `operator()`. + * + * Typically used with lambdas to construct type-matching visitors for e.g. std::variant: + * ``` + * std::variant var; + * Visit(TOverloaded{ + * [](int val) { Cerr << "int: " << val; }, + * [](void* val) { Cerr << "ptr: " << val; }, + * [](const TString& val) { Cerr << "str: " << val; }, + * }, var); + * ``` + * + * *** IMPORTANT NOTE (IMPLICIT ARGUMENT CONVERSIONS) *** + * + * Since the resulting objects use regular overloaded method resolution rules, + * methods may be called by inexact matches (causing implicit casts), hence this + * implementation does not guarantee exhaustiveness of cases. + * + * For example, the following code will compile and run by casting all values to + * double: + * ``` + * std::variant var; + * Visit(TOverloaded{ + * [](double val) { Cerr << "dbl: " << val; }, + * }, var); + * ``` + * + * If cases may be ambigous or specific type-matching logic is required, + * a verbose `if constexpr`-based version would be preferred: + * ``` + * std::variant var; + * Visit([](auto&& val) { + * using T = std::decay_t; + * if constexpr (std::is_same_v) { + * Cerr << "int: " << val; + * } else if constexpr (std::is_same_v) { + * Cerr << "dbl: " << val; + * } else if constexpr (std::is_same_v) { + * Cerr << "chr: " << val; + * } else { + * static_assert(TDependentFalse, "unexpected type"); + * } + * }, var); + * ``` + */ + +template +struct TOverloaded: Fs... { + using Fs::operator()...; +}; + +template +TOverloaded(Fs...) -> TOverloaded; diff --git a/util/generic/overloaded_ut.cpp b/util/generic/overloaded_ut.cpp new file mode 100644 index 00000000000..f3d73895ad9 --- /dev/null +++ b/util/generic/overloaded_ut.cpp @@ -0,0 +1,82 @@ +#include + +#include + +#include +#include + +#include + +namespace { + struct TType1 {}; + struct TType2 {}; + struct TType3 {}; +} + +Y_UNIT_TEST_SUITE(TOverloadedTest) { + Y_UNIT_TEST(StaticTest) { + auto f = TOverloaded{ + [](const TType1&) {}, + [](const TType2&) {}, + [](const TType3&) {}}; + using F = decltype(f); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + } + + Y_UNIT_TEST(VariantTest) { + std::variant v = 5; + int res = 0; + std::visit(TOverloaded{ + [&](int val) { res = val; }, + [&](double) { res = -1; }, + [&](TType1) { res = -1; }}, + v); + UNIT_ASSERT_VALUES_EQUAL(res, 5); + } + + Y_UNIT_TEST(TupleTest) { + std::tuple t{5, 3.14, true, 20}; + TString res; + + ForEach(t, TOverloaded{ + [&](int val) { res += "(int) " + ToString(val) + ' '; }, + [&](double val) { res += "(double) " + ToString(val) + ' '; }, + [&](bool val) { res += "(bool) " + ToString(val) + ' '; }, + }); + + UNIT_ASSERT_VALUES_EQUAL(res, "(int) 5 (double) 3.14 (bool) 1 (int) 20 "); + } + + Y_UNIT_TEST(ImplicitConversionsTest) { + using TTestVariant = std::variant; + + // Purposefully exhibit inexact overload matched with implicit type + // conversions + + // All cases implicitly cast to int + auto matchAsInt = [](TTestVariant var) { + return std::visit(TOverloaded{ + [](int val) { return val; }, + }, var); + }; + + UNIT_ASSERT_VALUES_EQUAL(matchAsInt(TTestVariant{17.77}), 17); + UNIT_ASSERT_VALUES_EQUAL(matchAsInt(TTestVariant{12345}), 12345); + UNIT_ASSERT_VALUES_EQUAL(matchAsInt(TTestVariant{'X'}), 88); + + // All cases implicitly cast to double + auto matchAsDouble = [](TTestVariant var) { + return std::visit(TOverloaded{ + [](double val) { return val; }, + }, var); + }; + + UNIT_ASSERT_VALUES_EQUAL(matchAsDouble(TTestVariant{17.77}), 17.77); + UNIT_ASSERT_VALUES_EQUAL(matchAsDouble(TTestVariant{12345}), 12345.0); + UNIT_ASSERT_VALUES_EQUAL(matchAsDouble(TTestVariant{'X'}), 88.0); + } +} diff --git a/util/generic/ptr.cpp b/util/generic/ptr.cpp new file mode 100644 index 00000000000..b29baebc175 --- /dev/null +++ b/util/generic/ptr.cpp @@ -0,0 +1,17 @@ +#include "ptr.h" + +#include +#include + +#include +#include + +void TFree::DoDestroy(void* t) noexcept { + free(t); +} + +void TDelete::Destroy(void* t) noexcept { + ::operator delete(t); +} + +TThrRefBase::~TThrRefBase() = default; diff --git a/util/generic/ptr.h b/util/generic/ptr.h new file mode 100644 index 00000000000..cc2e3c0f519 --- /dev/null +++ b/util/generic/ptr.h @@ -0,0 +1,1174 @@ +#pragma once + +#include "fwd.h" +#include "utility.h" +#include "intrlist.h" +#include "refcount.h" +#include "typetraits.h" +#include "singleton.h" + +#include +#include + +#include +#include +#include + +template +using TGuardConversion = typename std::enable_if_t::value>; + +template +inline void AssertTypeComplete() { + // If compiler triggers this error from destructor of your class with + // smart pointer, then may be you should move the destructor definition + // to the .cpp file, where type T have full definition. + // + // 'delete' called on pointer to incomplete type is + // undefined behavior (missing destructor call/corrupted memory manager). + // 'sizeof' is used to trigger compile-time error. + static_assert(sizeof(T) != 0, "Type must be complete"); +} + +template +inline void CheckedDelete(T* t) { + AssertTypeComplete(); + + delete t; +} + +template +inline void CheckedArrayDelete(T* t) { + AssertTypeComplete(); + + delete[] t; +} + +class TNoAction { +public: + template + static inline void Destroy(T*) noexcept { + } +}; + +class TDelete { +public: + template + static inline void Destroy(T* t) noexcept { + CheckedDelete(t); + } + + /* + * special handling for nullptr - call nothing + */ + static inline void Destroy(std::nullptr_t) noexcept { + } + + /* + * special handling for void* - call ::operator delete() + */ + static void Destroy(void* t) noexcept; +}; + +class TDeleteArray { +public: + template + static inline void Destroy(T* t) noexcept { + CheckedArrayDelete(t); + } +}; + +class TDestructor { +public: + template + static inline void Destroy(T* t) noexcept { + (void)t; + t->~T(); + } +}; + +class TFree { +public: + template + static inline void Destroy(T* t) noexcept { + DoDestroy((void*)t); + } + +private: + /* + * we do not want dependancy on cstdlib here... + */ + static void DoDestroy(void* t) noexcept; +}; + +template +class TPointerCommon { +public: + using TValueType = T; + + inline T* operator->() const noexcept { + T* ptr = AsT(); + Y_ASSERT(ptr); + return ptr; + } + +#ifndef __cpp_impl_three_way_comparison + template + inline bool operator==(const C& p) const noexcept { + return (p == AsT()); + } + + template + inline bool operator!=(const C& p) const noexcept { + return (p != AsT()); + } +#endif + + inline explicit operator bool() const noexcept { + return nullptr != AsT(); + } + +protected: + inline T* AsT() const noexcept { + return (static_cast(this))->Get(); + } + + static inline T* DoRelease(T*& t) noexcept { + T* ret = t; + t = nullptr; + return ret; + } +}; + +template +class TPointerBase: public TPointerCommon { +public: + inline T& operator*() const noexcept { + Y_ASSERT(this->AsT()); + + return *(this->AsT()); + } + + inline T& operator[](size_t n) const noexcept { + Y_ASSERT(this->AsT()); + + return (this->AsT())[n]; + } +}; + +/* + * void*-like pointers does not have operator* + */ +template +class TPointerBase: public TPointerCommon { +}; + +template +class TAutoPtr: public TPointerBase, T> { +public: + inline TAutoPtr(T* t = nullptr) noexcept + : T_(t) + { + } + + inline TAutoPtr(const TAutoPtr& t) noexcept + : T_(t.Release()) + { + } + + inline ~TAutoPtr() { + DoDestroy(); + } + + inline TAutoPtr& operator=(const TAutoPtr& t) noexcept { + if (this != &t) { + Reset(t.Release()); + } + + return *this; + } + + inline T* Release() const noexcept Y_WARN_UNUSED_RESULT { + return this->DoRelease(T_); + } + + Y_REINITIALIZES_OBJECT inline void Reset(T* t) noexcept { + if (T_ != t) { + DoDestroy(); + T_ = t; + } + } + + Y_REINITIALIZES_OBJECT inline void Reset() noexcept { + Destroy(); + } + + inline void Destroy() noexcept { + Reset(nullptr); + } + + inline void Swap(TAutoPtr& r) noexcept { + DoSwap(T_, r.T_); + } + + inline T* Get() const noexcept { + return T_; + } + +#ifdef __cpp_impl_three_way_comparison + template + inline bool operator==(const Other& p) const noexcept { + return (p == Get()); + } +#endif +private: + inline void DoDestroy() noexcept { + if (T_) { + D::Destroy(T_); + } + } + +private: + mutable T* T_; +}; + +template +class THolder: public TPointerBase, T> { +public: + constexpr THolder() noexcept + : T_(nullptr) + { + } + + constexpr THolder(std::nullptr_t) noexcept + : T_(nullptr) + { + } + + explicit THolder(T* t) noexcept + : T_(t) + { + } + + inline THolder(TAutoPtr t) noexcept + : T_(t.Release()) + { + } + + template > + inline THolder(TAutoPtr t) noexcept + : T_(t.Release()) + { + } + + inline THolder(THolder&& that) noexcept + : T_(that.Release()) + { + } + + template > + inline THolder(THolder&& that) noexcept + : T_(that.Release()) + { + } + + THolder(const THolder&) = delete; + THolder& operator=(const THolder&) = delete; + + inline ~THolder() { + DoDestroy(); + } + + inline void Destroy() noexcept { + Reset(nullptr); + } + + inline T* Release() noexcept Y_WARN_UNUSED_RESULT { + return this->DoRelease(T_); + } + + Y_REINITIALIZES_OBJECT inline void Reset(T* t) noexcept { + if (T_ != t) { + DoDestroy(); + T_ = t; + } + } + + Y_REINITIALIZES_OBJECT inline void Reset(TAutoPtr t) noexcept { + Reset(t.Release()); + } + + Y_REINITIALIZES_OBJECT inline void Reset() noexcept { + Destroy(); + } + + inline void Swap(THolder& r) noexcept { + DoSwap(T_, r.T_); + } + + inline T* Get() const noexcept { + return T_; + } + + inline operator TAutoPtr() noexcept { + return Release(); + } + + THolder& operator=(std::nullptr_t) noexcept { + this->Reset(nullptr); + return *this; + } + + THolder& operator=(THolder&& that) noexcept { + this->Reset(that.Release()); + return *this; + } + + template + THolder& operator=(THolder&& that) noexcept { + this->Reset(that.Release()); + return *this; + } + +#ifdef __cpp_impl_three_way_comparison + template + inline bool operator==(const Other& p) const noexcept { + return (p == Get()); + } +#endif +private: + inline void DoDestroy() noexcept { + if (T_) { + D::Destroy(T_); + } + } + +private: + T* T_; +}; + +template +[[nodiscard]] THolder MakeHolder(Args&&... args) { + return THolder(new T(std::forward(args)...)); +} + +/* + * usage: + * class T: public TRefCounted + * and we get methods Ref() && UnRef() with + * proper destruction of last UnRef() + */ +template +class TRefCounted { +public: + inline TRefCounted(long initval = 0) noexcept + : Counter_(initval) + { + } + + inline ~TRefCounted() = default; + + inline void Ref(intptr_t d) noexcept { + auto resultCount = Counter_.Add(d); + Y_ASSERT(resultCount >= d); + (void)resultCount; + } + + inline void Ref() noexcept { + auto resultCount = Counter_.Inc(); + Y_ASSERT(resultCount != 0); + (void)resultCount; + } + + inline void UnRef(intptr_t d) noexcept { + auto resultCount = Counter_.Sub(d); + Y_ASSERT(resultCount >= 0); + if (resultCount == 0) { + D::Destroy(static_cast(this)); + } + } + + inline void UnRef() noexcept { + UnRef(1); + } + + inline intptr_t RefCount() const noexcept { + return Counter_.Val(); + } + + inline void DecRef() noexcept { + auto resultCount = Counter_.Dec(); + Y_ASSERT(resultCount >= 0); + (void)resultCount; + } + + TRefCounted(const TRefCounted&) + : Counter_(0) + { + } + + void operator=(const TRefCounted&) { + } + +private: + C Counter_; +}; + +/** + * Atomically reference-counted base with a virtual destructor. + * + * @note Plays well with inheritance, should be used for refcounted base classes. + */ +struct TThrRefBase: public TRefCounted { + virtual ~TThrRefBase(); +}; + +/** + * Atomically reference-counted base. + * + * Deletes refcounted object as type T. + * + * @warning Additional care should be taken with regard to inheritance. If used + * as a base class, @p T should either declare a virtual destructor, or be + * derived from @p TThrRefBase instead. Otherwise, only destructor of class @p T + * would be called, potentially slicing the object and creating memory leaks. + * + * @note To avoid accidental inheritance when it is not originally intended, + * class @p T should be marked as final. + */ +template +using TAtomicRefCount = TRefCounted; + +/** + * Non-atomically reference-counted base. + * + * @warning Not thread-safe. Use with great care. If in doubt, use @p ThrRefBase + * or @p TAtomicRefCount instead. + */ +template +using TSimpleRefCount = TRefCounted; + +template +class TDefaultIntrusivePtrOps { +public: + static inline void Ref(T* t) noexcept { + Y_ASSERT(t); + + t->Ref(); + } + + static inline void UnRef(T* t) noexcept { + Y_ASSERT(t); + + t->UnRef(); + } + + static inline void DecRef(T* t) noexcept { + Y_ASSERT(t); + + t->DecRef(); + } + + static inline long RefCount(const T* t) noexcept { + Y_ASSERT(t); + + return t->RefCount(); + } +}; + +template +class TIntrusivePtr: public TPointerBase, T> { + template + friend class TIntrusivePtr; + + template + friend class TIntrusiveConstPtr; + +public: + struct TNoIncrement { + }; + + inline TIntrusivePtr(T* t = nullptr) noexcept + : T_(t) + { + Ops(); + Ref(); + } + + inline TIntrusivePtr(T* t, TNoIncrement) noexcept + : T_(t) + { + Ops(); + } + + inline ~TIntrusivePtr() { + UnRef(); + } + + inline TIntrusivePtr(const TIntrusivePtr& p) noexcept + : T_(p.T_) + { + Ref(); + } + + // NOTE: + // without std::enable_if_t compiler sometimes tries to use this constructor inappropriately + // e.g. + // struct A {}; + // struct B {}; + // void Func(TIntrusivePtr); + // void Func(TIntrusivePtr); + // ... + // Func(TIntrusivePtr(new A)); // <--- compiler can't decide which version of Func to use + template > + inline TIntrusivePtr(const TIntrusivePtr& p) noexcept + : T_(p.Get()) + { + Ref(); + } + + template > + inline TIntrusivePtr(TIntrusivePtr&& p) noexcept + : T_(p.T_) + { + p.T_ = nullptr; + } + + inline TIntrusivePtr(TIntrusivePtr&& p) noexcept + : T_(nullptr) + { + Swap(p); + } + + inline TIntrusivePtr& operator=(TIntrusivePtr p) noexcept { + p.Swap(*this); + + return *this; + } + + // Effectively replace both: + // Reset(const TIntrusivePtr&) + // Reset(TIntrusivePtr&&) + Y_REINITIALIZES_OBJECT inline void Reset(TIntrusivePtr t) noexcept { + Swap(t); + } + + Y_REINITIALIZES_OBJECT inline void Reset() noexcept { + Drop(); + } + + inline T* Get() const noexcept { + return T_; + } + + inline void Swap(TIntrusivePtr& r) noexcept { + DoSwap(T_, r.T_); + } + + inline void Drop() noexcept { + TIntrusivePtr(nullptr).Swap(*this); + } + + inline T* Release() const noexcept Y_WARN_UNUSED_RESULT { + T* res = T_; + if (T_) { + Ops::DecRef(T_); + T_ = nullptr; + } + return res; + } + + inline long RefCount() const noexcept { + return T_ ? Ops::RefCount(T_) : 0; + } + +#ifdef __cpp_impl_three_way_comparison + template + inline bool operator==(const Other& p) const noexcept { + return (p == Get()); + } +#endif +private: + inline void Ref() noexcept { + if (T_) { + Ops::Ref(T_); + } + } + + inline void UnRef() noexcept { + if (T_) { + Ops::UnRef(T_); + } + } + +private: + mutable T* T_; +}; + +template +struct THash>: THash { + using THash::operator(); + inline size_t operator()(const TIntrusivePtr& ptr) const { + return THash::operator()(ptr.Get()); + } +}; + +// Behaves like TIntrusivePtr but returns const T* to prevent user from accidentally modifying the referenced object. +template +class TIntrusiveConstPtr: public TPointerBase, const T> { +public: + inline TIntrusiveConstPtr(T* t = nullptr) noexcept // we need a non-const pointer to Ref(), UnRef() and eventually delete it. + : T_(t) + { + Ops(); + Ref(); + } + + inline ~TIntrusiveConstPtr() { + UnRef(); + } + + inline TIntrusiveConstPtr(const TIntrusiveConstPtr& p) noexcept + : T_(p.T_) + { + Ref(); + } + + inline TIntrusiveConstPtr(TIntrusiveConstPtr&& p) noexcept + : T_(nullptr) + { + Swap(p); + } + + inline TIntrusiveConstPtr(TIntrusivePtr p) noexcept + : T_(p.T_) + { + p.T_ = nullptr; + } + + template > + inline TIntrusiveConstPtr(const TIntrusiveConstPtr& p) noexcept + : T_(p.T_) + { + Ref(); + } + + template > + inline TIntrusiveConstPtr(TIntrusiveConstPtr&& p) noexcept + : T_(p.T_) + { + p.T_ = nullptr; + } + + inline TIntrusiveConstPtr& operator=(TIntrusiveConstPtr p) noexcept { + p.Swap(*this); + + return *this; + } + + // Effectively replace both: + // Reset(const TIntrusiveConstPtr&) + // Reset(TIntrusiveConstPtr&&) + Y_REINITIALIZES_OBJECT inline void Reset(TIntrusiveConstPtr t) noexcept { + Swap(t); + } + + Y_REINITIALIZES_OBJECT inline void Reset() noexcept { + Drop(); + } + + inline const T* Get() const noexcept { + return T_; + } + + inline void Swap(TIntrusiveConstPtr& r) noexcept { + DoSwap(T_, r.T_); + } + + inline void Drop() noexcept { + TIntrusiveConstPtr(nullptr).Swap(*this); + } + + inline long RefCount() const noexcept { + return T_ ? Ops::RefCount(T_) : 0; + } + +#ifdef __cpp_impl_three_way_comparison + template + inline bool operator==(const Other& p) const noexcept { + return (p == Get()); + } +#endif +private: + inline void Ref() noexcept { + if (T_ != nullptr) { + Ops::Ref(T_); + } + } + + inline void UnRef() noexcept { + if (T_ != nullptr) { + Ops::UnRef(T_); + } + } + +private: + T* T_; + + template + friend class TIntrusiveConstPtr; +}; + +template +struct THash>: THash { + using THash::operator(); + inline size_t operator()(const TIntrusiveConstPtr& ptr) const { + return THash::operator()(ptr.Get()); + } +}; + +template +class TSimpleIntrusiveOps { + using TFunc = void (*)(T*) +#if __cplusplus >= 201703 + noexcept +#endif + ; + + static void DoRef(T* t) noexcept { + Ops::Ref(t); + } + + static void DoUnRef(T* t) noexcept { + Ops::UnRef(t); + } + +public: + inline TSimpleIntrusiveOps() noexcept { + InitStaticOps(); + } + + inline ~TSimpleIntrusiveOps() = default; + + static inline void Ref(T* t) noexcept { + Ref_(t); + } + + static inline void UnRef(T* t) noexcept { + UnRef_(t); + } + +private: + static inline void InitStaticOps() noexcept { + struct TInit { + inline TInit() noexcept { + Ref_ = DoRef; + UnRef_ = DoUnRef; + } + }; + + Singleton(); + } + +private: + static TFunc Ref_; + static TFunc UnRef_; +}; + +template +typename TSimpleIntrusiveOps::TFunc TSimpleIntrusiveOps::Ref_ = nullptr; + +template +typename TSimpleIntrusiveOps::TFunc TSimpleIntrusiveOps::UnRef_ = nullptr; + +template , typename... Args> +[[nodiscard]] TIntrusivePtr MakeIntrusive(Args&&... args) { + return new T{std::forward(args)...}; +} + +template , typename... Args> +[[nodiscard]] TIntrusiveConstPtr MakeIntrusiveConst(Args&&... args) { + return new T{std::forward(args)...}; +} + +template +class TSharedPtr: public TPointerBase, T> { + template + friend class TSharedPtr; + +public: + inline TSharedPtr() noexcept + : T_(nullptr) + , C_(nullptr) + { + } + + inline TSharedPtr(T* t) { + THolder h(t); + + Init(h); + } + + inline TSharedPtr(TAutoPtr t) { + Init(t); + } + + inline TSharedPtr(T* t, C* c) noexcept + : T_(t) + , C_(c) + { + } + + template > + inline TSharedPtr(THolder&& t) { + Init(t); + } + + inline ~TSharedPtr() { + UnRef(); + } + + inline TSharedPtr(const TSharedPtr& t) noexcept + : T_(t.T_) + , C_(t.C_) + { + Ref(); + } + + inline TSharedPtr(TSharedPtr&& t) noexcept + : T_(nullptr) + , C_(nullptr) + { + Swap(t); + } + + template > + inline TSharedPtr(const TSharedPtr& t) noexcept + : T_(t.T_) + , C_(t.C_) + { + Ref(); + } + + template > + inline TSharedPtr(TSharedPtr&& t) noexcept + : T_(t.T_) + , C_(t.C_) + { + t.T_ = nullptr; + t.C_ = nullptr; + } + + inline TSharedPtr& operator=(TSharedPtr t) noexcept { + t.Swap(*this); + + return *this; + } + + // Effectively replace both: + // Reset(const TSharedPtr& t) + // Reset(TSharedPtr&& t) + Y_REINITIALIZES_OBJECT inline void Reset(TSharedPtr t) noexcept { + Swap(t); + } + + Y_REINITIALIZES_OBJECT inline void Reset() noexcept { + Drop(); + } + + inline void Drop() noexcept { + TSharedPtr().Swap(*this); + } + + inline T* Get() const noexcept { + return T_; + } + + inline C* ReferenceCounter() const noexcept { + return C_; + } + + inline void Swap(TSharedPtr& r) noexcept { + DoSwap(T_, r.T_); + DoSwap(C_, r.C_); + } + + inline long RefCount() const noexcept { + return C_ ? C_->Val() : 0; + } + + template + [[nodiscard]] inline TSharedPtr As() & noexcept { + static_assert(std::has_virtual_destructor(), "Type should have a virtual dtor"); + static_assert(std::is_base_of(), "When downcasting from T to TT, T should be a parent of TT"); + if (const auto ttPtr = dynamic_cast(T_)) { + TSharedPtr ttSharedPtr(ttPtr, C_); + ttSharedPtr.Ref(); + return ttSharedPtr; + } else { + return TSharedPtr{}; + } + } + + template + [[nodiscard]] inline TSharedPtr As() && noexcept { + static_assert(std::has_virtual_destructor(), "Type should have a virtual dtor"); + static_assert(std::is_base_of(), "When downcasting from T to TT, T should be a parent of TT"); + if (const auto ttPtr = dynamic_cast(T_)) { + TSharedPtr ttSharedPtr(ttPtr, C_); + T_ = nullptr; + C_ = nullptr; + return ttSharedPtr; + } else { + return TSharedPtr{}; + } + } + +#ifdef __cpp_impl_three_way_comparison + template + inline bool operator==(const Other& p) const noexcept { + return (p == Get()); + } +#endif +private: + template + inline void Init(X& t) { + C_ = !!t ? new C(1) : nullptr; + T_ = t.Release(); + } + + inline void Ref() noexcept { + if (C_) { + C_->Inc(); + } + } + + inline void UnRef() noexcept { + if (C_ && !C_->Dec()) { + DoDestroy(); + } + } + + inline void DoDestroy() noexcept { + if (T_) { + D::Destroy(T_); + } + + delete C_; + } + +private: + T* T_; + C* C_; +}; + +template +struct THash>: THash { + using THash::operator(); + inline size_t operator()(const TSharedPtr& ptr) const { + return THash::operator()(ptr.Get()); + } +}; + +template +using TAtomicSharedPtr = TSharedPtr; + +// use with great care. if in doubt, use TAtomicSharedPtr instead +template +using TSimpleSharedPtr = TSharedPtr; + +template +[[nodiscard]] TSharedPtr MakeShared(Args&&... args) { + return new T{std::forward(args)...}; +} + +template +[[nodiscard]] inline TAtomicSharedPtr MakeAtomicShared(Args&&... args) { + return MakeShared(std::forward(args)...); +} + +template +[[nodiscard]] inline TSimpleSharedPtr MakeSimpleShared(Args&&... args) { + return MakeShared(std::forward(args)...); +} + +class TCopyClone { +public: + template + static inline T* Copy(T* t) { + if (t) + return t->Clone(); + return nullptr; + } +}; + +class TCopyNew { +public: + template + static inline T* Copy(T* t) { + if (t) + return new T(*t); + return nullptr; + } +}; + +template +class TCopyPtr: public TPointerBase, T> { +public: + inline TCopyPtr(T* t = nullptr) noexcept + : T_(t) + { + } + + inline TCopyPtr(const TCopyPtr& t) + : T_(C::Copy(t.Get())) + { + } + + inline TCopyPtr(TCopyPtr&& t) noexcept + : T_(nullptr) + { + Swap(t); + } + + inline ~TCopyPtr() { + DoDestroy(); + } + + inline TCopyPtr& operator=(TCopyPtr t) noexcept { + t.Swap(*this); + + return *this; + } + + inline T* Release() noexcept Y_WARN_UNUSED_RESULT { + return DoRelease(T_); + } + + Y_REINITIALIZES_OBJECT inline void Reset(T* t) noexcept { + if (T_ != t) { + DoDestroy(); + T_ = t; + } + } + + Y_REINITIALIZES_OBJECT inline void Reset() noexcept { + Destroy(); + } + + inline void Destroy() noexcept { + Reset(nullptr); + } + + inline void Swap(TCopyPtr& r) noexcept { + DoSwap(T_, r.T_); + } + + inline T* Get() const noexcept { + return T_; + } + +#ifdef __cpp_impl_three_way_comparison + template + inline bool operator==(const Other& p) const noexcept { + return (p == Get()); + } +#endif +private: + inline void DoDestroy() noexcept { + if (T_) + D::Destroy(T_); + } + +private: + T* T_; +}; + +// Copy-on-write pointer +template +class TCowPtr: public TPointerBase, const typename TPtr::TValueType> { + using T = typename TPtr::TValueType; + +public: + inline TCowPtr() = default; + + inline TCowPtr(const TPtr& p) + : T_(p) + { + } + + inline TCowPtr(T* p) + : T_(p) + { + } + + inline const T* Get() const noexcept { + return Const(); + } + + inline const T* Const() const noexcept { + return T_.Get(); + } + + inline T* Mutable() { + Unshare(); + + return T_.Get(); + } + + inline bool Shared() const noexcept { + return T_.RefCount() > 1; + } + + inline void Swap(TCowPtr& r) noexcept { + T_.Swap(r.T_); + } + + Y_REINITIALIZES_OBJECT inline void Reset(TCowPtr p) { + p.Swap(*this); + } + + Y_REINITIALIZES_OBJECT inline void Reset() { + T_.Reset(); + } + +#ifdef __cpp_impl_three_way_comparison + template + inline bool operator==(const Other& p) const noexcept { + return (p == Get()); + } +#endif +private: + inline void Unshare() { + if (Shared()) { + Reset(TCopy::Copy(T_.Get())); + } + } + +private: + TPtr T_; +}; + +// saves .Get() on argument passing. Intended usage: Func(TPtrArg p); ... TIntrusivePtr p2; Func(p2); +template +class TPtrArg { + T* Ptr; + +public: + TPtrArg(T* p) + : Ptr(p) + { + } + TPtrArg(const TIntrusivePtr& p) + : Ptr(p.Get()) + { + } + operator T*() const { + return Ptr; + } + T* operator->() const { + return Ptr; + } + T* Get() const { + return Ptr; + } +}; diff --git a/util/generic/ptr.pxd b/util/generic/ptr.pxd new file mode 100644 index 00000000000..16e8d191443 --- /dev/null +++ b/util/generic/ptr.pxd @@ -0,0 +1,42 @@ +cdef extern from "" nogil: + cdef cppclass THolder[T]: + THolder(...) + T* Get() + void Destroy() + T* Release() + void Reset() + void Reset(T*) + void Swap(THolder[T]) + + + cdef THolder[T] MakeHolder[T](...) + + + cdef cppclass TIntrusivePtr[T]: + TIntrusivePtr() + TIntrusivePtr(T*) + TIntrusivePtr& operator=(...) + void Reset(T*) + T* Get() + T* Release() + void Drop() + + + cdef cppclass TIntrusiveConstPtr[T]: + TIntrusiveConstPtr() + TIntrusiveConstPtr(T*) + TIntrusiveConstPtr& operator=(...) + void Reset(T*) + const T* Get() + void Drop() + + + cdef cppclass TAtomicSharedPtr[T]: + TAtomicSharedPtr() + TAtomicSharedPtr(T*) + T& operator*() + T* Get() + void Reset(T*) + + + cdef TAtomicSharedPtr[T] MakeAtomicShared[T](...) diff --git a/util/generic/ptr_ut.cpp b/util/generic/ptr_ut.cpp new file mode 100644 index 00000000000..1e84e4fadb9 --- /dev/null +++ b/util/generic/ptr_ut.cpp @@ -0,0 +1,936 @@ +#include "ptr.h" +#include "vector.h" +#include "noncopyable.h" + +#include +#include + +#include +#include +#include + +class TPointerTest: public TTestBase { + UNIT_TEST_SUITE(TPointerTest); + UNIT_TEST(TestTypedefs); + UNIT_TEST(TestSimpleIntrPtr); + UNIT_TEST(TestHolderPtr); + UNIT_TEST(TestHolderPtrMoveConstructor); + UNIT_TEST(TestHolderPtrMoveConstructorInheritance); + UNIT_TEST(TestHolderPtrMoveAssignment); + UNIT_TEST(TestHolderPtrMoveAssignmentInheritance); + UNIT_TEST(TestMakeHolder); + UNIT_TEST(TestTrulePtr); + UNIT_TEST(TestAutoToHolder); + UNIT_TEST(TestCopyPtr); + UNIT_TEST(TestIntrPtr); + UNIT_TEST(TestIntrusiveConvertion); + UNIT_TEST(TestIntrusiveConstConvertion); + UNIT_TEST(TestIntrusiveConstConstruction); + UNIT_TEST(TestMakeIntrusive); + UNIT_TEST(TestCopyOnWritePtr1); + UNIT_TEST(TestCopyOnWritePtr2); + UNIT_TEST(TestOperatorBool); + UNIT_TEST(TestMakeShared); + UNIT_TEST(TestComparison); + UNIT_TEST(TestSimpleIntrusivePtrCtorTsan); + UNIT_TEST(TestRefCountedPtrsInHashSet); + UNIT_TEST(TestSharedPtrDowncast); + UNIT_TEST_SUITE_END(); + +private: + void TestSimpleIntrusivePtrCtorTsan() { + struct S: public TAtomicRefCount { + }; + + struct TLocalThread: public ISimpleThread { + void* ThreadProc() override { + TSimpleIntrusivePtr ptr; + return nullptr; + } + }; + + // Create TSimpleIntrusivePtr in different threads + // Its constructor should be thread-safe + + TLocalThread t1, t2; + + t1.Start(); + t2.Start(); + t1.Join(); + t2.Join(); + } + + inline void TestTypedefs() { + TAtomicSharedPtr(new int(1)); + TSimpleSharedPtr(new int(1)); + } + + void TestSimpleIntrPtr(); + void TestHolderPtr(); + void TestHolderPtrMoveConstructor(); + void TestHolderPtrMoveConstructorInheritance(); + void TestHolderPtrMoveAssignment(); + void TestHolderPtrMoveAssignmentInheritance(); + void TestMakeHolder(); + void TestTrulePtr(); + void TestAutoToHolder(); + void TestCopyPtr(); + void TestIntrPtr(); + void TestIntrusiveConvertion(); + void TestIntrusiveConstConvertion(); + void TestIntrusiveConstConstruction(); + void TestMakeIntrusive(); + void TestCopyOnWritePtr1(); + void TestCopyOnWritePtr2(); + void TestOperatorBool(); + void TestMakeShared(); + void TestComparison(); + template + void TestRefCountedPtrsInHashSetImpl(); + void TestRefCountedPtrsInHashSet(); + void TestSharedPtrDowncast(); +}; + +UNIT_TEST_SUITE_REGISTRATION(TPointerTest); + +static int cnt = 0; + +class A: public TAtomicRefCount { +public: + inline A() { + ++cnt; + } + + inline A(const A&) + : TAtomicRefCount(*this) + { + ++cnt; + } + + inline ~A() { + --cnt; + } +}; + +static A* MakeA() { + return new A(); +} + +/* + * test compileability + */ +class B; +static TSimpleIntrusivePtr GetB() { + throw 1; +} + +void Func() { + TSimpleIntrusivePtr b = GetB(); +} + +void TPointerTest::TestSimpleIntrPtr() { + { + TSimpleIntrusivePtr a1(MakeA()); + TSimpleIntrusivePtr a2(MakeA()); + TSimpleIntrusivePtr a3 = a2; + + a1 = a2; + a2 = a3; + } + + UNIT_ASSERT_VALUES_EQUAL(cnt, 0); +} + +void TPointerTest::TestHolderPtr() { + { + THolder a1(MakeA()); + THolder a2(a1.Release()); + } + + UNIT_ASSERT_VALUES_EQUAL(cnt, 0); +} + +THolder CreateInt(int value) { + THolder res(new int); + *res = value; + return res; +} + +void TPointerTest::TestHolderPtrMoveConstructor() { + THolder h = CreateInt(42); + UNIT_ASSERT_VALUES_EQUAL(*h, 42); +} + +void TPointerTest::TestHolderPtrMoveAssignment() { + THolder h(new int); + h = CreateInt(42); + UNIT_ASSERT_VALUES_EQUAL(*h, 42); +} + +struct TBase { + virtual ~TBase() = default; +}; + +struct TDerived: public TBase { +}; + +void TPointerTest::TestHolderPtrMoveConstructorInheritance() { + // compileability test + THolder basePtr(THolder(new TDerived)); +} + +void TPointerTest::TestHolderPtrMoveAssignmentInheritance() { + // compileability test + THolder basePtr; + basePtr = THolder(new TDerived); +} + +void TPointerTest::TestMakeHolder() { + { + auto ptr = MakeHolder(5); + UNIT_ASSERT_VALUES_EQUAL(*ptr, 5); + } + { + struct TRec { + int X, Y; + TRec() + : X(1) + , Y(2) + { + } + }; + THolder ptr = MakeHolder(); + UNIT_ASSERT_VALUES_EQUAL(ptr->X, 1); + UNIT_ASSERT_VALUES_EQUAL(ptr->Y, 2); + } + { + struct TRec { + int X, Y; + TRec(int x, int y) + : X(x) + , Y(y) + { + } + }; + auto ptr = MakeHolder(1, 2); + UNIT_ASSERT_VALUES_EQUAL(ptr->X, 1); + UNIT_ASSERT_VALUES_EQUAL(ptr->Y, 2); + } + { + class TRec { + private: + int X_, Y_; + + public: + TRec(int x, int y) + : X_(x) + , Y_(y) + { + } + + int GetX() const { + return X_; + } + int GetY() const { + return Y_; + } + }; + auto ptr = MakeHolder(1, 2); + UNIT_ASSERT_VALUES_EQUAL(ptr->GetX(), 1); + UNIT_ASSERT_VALUES_EQUAL(ptr->GetY(), 2); + } +} + +void TPointerTest::TestTrulePtr() { + { + TAutoPtr a1(MakeA()); + TAutoPtr a2(a1); + a1 = a2; + } + + UNIT_ASSERT_VALUES_EQUAL(cnt, 0); +} + +void TPointerTest::TestAutoToHolder() { + { + TAutoPtr a1(MakeA()); + THolder a2(a1); + + UNIT_ASSERT_EQUAL(a1.Get(), nullptr); + UNIT_ASSERT_VALUES_EQUAL(cnt, 1); + } + + UNIT_ASSERT_VALUES_EQUAL(cnt, 0); + + { + TAutoPtr x(new A()); + THolder y = x; + } + + UNIT_ASSERT_VALUES_EQUAL(cnt, 0); + + { + class B1: public A { + }; + + TAutoPtr x(new B1()); + THolder y = x; + } + + UNIT_ASSERT_VALUES_EQUAL(cnt, 0); +} + +void TPointerTest::TestCopyPtr() { + TCopyPtr a1(MakeA()); + { + TCopyPtr a2(MakeA()); + TCopyPtr a3 = a2; + UNIT_ASSERT_VALUES_EQUAL(cnt, 3); + + a1 = a2; + a2 = a3; + } + UNIT_ASSERT_VALUES_EQUAL(cnt, 1); + a1.Destroy(); + + UNIT_ASSERT_VALUES_EQUAL(cnt, 0); +} + +class TOp: public TSimpleRefCount, public TNonCopyable { +public: + static int Cnt; + +public: + TOp() { + ++Cnt; + } + virtual ~TOp() { + --Cnt; + } +}; + +int TOp::Cnt = 0; + +class TOp2: public TOp { +public: + TIntrusivePtr Op; + +public: + TOp2(const TIntrusivePtr& op) + : Op(op) + { + ++Cnt; + } + ~TOp2() override { + --Cnt; + } +}; + +class TOp3 { +public: + TIntrusivePtr Op2; +}; + +void Attach(TOp3* op3, TIntrusivePtr* op) { + TIntrusivePtr op2 = new TOp2(*op); + op3->Op2 = op2.Get(); + *op = op2.Get(); +} + +void TPointerTest::TestIntrPtr() { + { + TIntrusivePtr p, p2; + TOp3 op3; + { + TVector> f1; + { + TVector> f2; + f2.push_back(new TOp); + p = new TOp; + f2.push_back(p); + Attach(&op3, &f2[1]); + f1 = f2; + UNIT_ASSERT_VALUES_EQUAL(f1[0]->RefCount(), 2); + UNIT_ASSERT_VALUES_EQUAL(f1[1]->RefCount(), 3); + UNIT_ASSERT_EQUAL(f1[1].Get(), op3.Op2.Get()); + UNIT_ASSERT_VALUES_EQUAL(op3.Op2->RefCount(), 3); + UNIT_ASSERT_VALUES_EQUAL(op3.Op2->Op->RefCount(), 2); + UNIT_ASSERT_VALUES_EQUAL(TOp::Cnt, 4); + } + p2 = p; + } + UNIT_ASSERT_VALUES_EQUAL(op3.Op2->RefCount(), 1); + UNIT_ASSERT_VALUES_EQUAL(op3.Op2->Op->RefCount(), 3); + UNIT_ASSERT_VALUES_EQUAL(TOp::Cnt, 3); + } + UNIT_ASSERT_VALUES_EQUAL(TOp::Cnt, 0); +} + +namespace NTestIntrusiveConvertion { + struct TA: public TSimpleRefCount { + }; + struct TAA: public TA { + }; + struct TB: public TSimpleRefCount { + }; + + void Func(TIntrusivePtr) { + } + + void Func(TIntrusivePtr) { + } + + void Func(TIntrusiveConstPtr) { + } + + void Func(TIntrusiveConstPtr) { + } +} + +void TPointerTest::TestIntrusiveConvertion() { + using namespace NTestIntrusiveConvertion; + + TIntrusivePtr aa = new TAA; + + UNIT_ASSERT_VALUES_EQUAL(aa->RefCount(), 1); + TIntrusivePtr a = aa; + UNIT_ASSERT_VALUES_EQUAL(aa->RefCount(), 2); + UNIT_ASSERT_VALUES_EQUAL(a->RefCount(), 2); + aa.Reset(); + UNIT_ASSERT_VALUES_EQUAL(a->RefCount(), 1); + + // test that Func(TIntrusivePtr) doesn't participate in overload resolution + Func(aa); +} + +void TPointerTest::TestIntrusiveConstConvertion() { + using namespace NTestIntrusiveConvertion; + + TIntrusiveConstPtr aa = new TAA; + + UNIT_ASSERT_VALUES_EQUAL(aa->RefCount(), 1); + TIntrusiveConstPtr a = aa; + UNIT_ASSERT_VALUES_EQUAL(aa->RefCount(), 2); + UNIT_ASSERT_VALUES_EQUAL(a->RefCount(), 2); + aa.Reset(); + UNIT_ASSERT_VALUES_EQUAL(a->RefCount(), 1); + + // test that Func(TIntrusiveConstPtr) doesn't participate in overload resolution + Func(aa); +} + +void TPointerTest::TestMakeIntrusive() { + { + UNIT_ASSERT_VALUES_EQUAL(0, TOp::Cnt); + auto p = MakeIntrusive(); + UNIT_ASSERT_VALUES_EQUAL(1, p->RefCount()); + UNIT_ASSERT_VALUES_EQUAL(1, TOp::Cnt); + } + UNIT_ASSERT_VALUES_EQUAL(TOp::Cnt, 0); +} + +void TPointerTest::TestCopyOnWritePtr1() { + using TPtr = TCowPtr>; + TPtr p1; + UNIT_ASSERT(!p1.Shared()); + + p1.Reset(new int(123)); + UNIT_ASSERT(!p1.Shared()); + + { + TPtr pTmp = p1; + + UNIT_ASSERT(p1.Shared()); + UNIT_ASSERT(pTmp.Shared()); + UNIT_ASSERT_EQUAL(p1.Get(), pTmp.Get()); + } + + UNIT_ASSERT(!p1.Shared()); + + TPtr p2 = p1; + TPtr p3; + p3 = p2; + + UNIT_ASSERT(p2.Shared()); + UNIT_ASSERT(p3.Shared()); + UNIT_ASSERT_EQUAL(p1.Get(), p2.Get()); + UNIT_ASSERT_EQUAL(p1.Get(), p3.Get()); + + *(p1.Mutable()) = 456; + + UNIT_ASSERT(!p1.Shared()); + UNIT_ASSERT(p2.Shared()); + UNIT_ASSERT(p3.Shared()); + UNIT_ASSERT_EQUAL(*p1, 456); + UNIT_ASSERT_EQUAL(*p2, 123); + UNIT_ASSERT_EQUAL(*p3, 123); + UNIT_ASSERT_UNEQUAL(p1.Get(), p2.Get()); + UNIT_ASSERT_EQUAL(p2.Get(), p3.Get()); + + p2.Mutable(); + + UNIT_ASSERT(!p2.Shared()); + UNIT_ASSERT(!p3.Shared()); + UNIT_ASSERT_EQUAL(*p2, 123); + UNIT_ASSERT_EQUAL(*p3, 123); + UNIT_ASSERT_UNEQUAL(p2.Get(), p3.Get()); +} + +struct X: public TSimpleRefCount { + inline X(int v = 0) + : V(v) + { + } + + int V; +}; + +void TPointerTest::TestCopyOnWritePtr2() { + using TPtr = TCowPtr>; + TPtr p1; + UNIT_ASSERT(!p1.Shared()); + + p1.Reset(new X(123)); + UNIT_ASSERT(!p1.Shared()); + + { + TPtr pTmp = p1; + + UNIT_ASSERT(p1.Shared()); + UNIT_ASSERT(pTmp.Shared()); + UNIT_ASSERT_EQUAL(p1.Get(), pTmp.Get()); + } + + UNIT_ASSERT(!p1.Shared()); + + TPtr p2 = p1; + TPtr p3; + p3 = p2; + + UNIT_ASSERT(p2.Shared()); + UNIT_ASSERT(p3.Shared()); + UNIT_ASSERT_EQUAL(p1.Get(), p2.Get()); + UNIT_ASSERT_EQUAL(p1.Get(), p3.Get()); + + p1.Mutable()->V = 456; + + UNIT_ASSERT(!p1.Shared()); + UNIT_ASSERT(p2.Shared()); + UNIT_ASSERT(p3.Shared()); + UNIT_ASSERT_EQUAL(p1->V, 456); + UNIT_ASSERT_EQUAL(p2->V, 123); + UNIT_ASSERT_EQUAL(p3->V, 123); + UNIT_ASSERT_UNEQUAL(p1.Get(), p2.Get()); + UNIT_ASSERT_EQUAL(p2.Get(), p3.Get()); + + p2.Mutable(); + + UNIT_ASSERT(!p2.Shared()); + UNIT_ASSERT(!p3.Shared()); + UNIT_ASSERT_EQUAL(p2->V, 123); + UNIT_ASSERT_EQUAL(p3->V, 123); + UNIT_ASSERT_UNEQUAL(p2.Get(), p3.Get()); +} + +namespace { + template + struct TImplicitlyCastable { + struct RTYes { + char t[2]; + }; + + using RTNo = char; + + static RTYes Func(TTo); + static RTNo Func(...); + static TFrom Get(); + + /* + * Result == (TFrom could be converted to TTo implicitly) + */ + enum { + Result = (sizeof(Func(Get())) != sizeof(RTNo)) + }; + }; + + struct TImplicitlyCastableToBool { + inline operator bool() const { + return true; + } + }; + +} + +void TPointerTest::TestOperatorBool() { + using TVec = TVector; + + // to be sure TImplicitlyCastable works as expected + UNIT_ASSERT((TImplicitlyCastable::Result)); + UNIT_ASSERT((TImplicitlyCastable::Result)); + UNIT_ASSERT((TImplicitlyCastable::Result)); + UNIT_ASSERT(!(TImplicitlyCastable::Result)); + UNIT_ASSERT((TImplicitlyCastable::Result)); + UNIT_ASSERT((TImplicitlyCastable::Result)); + UNIT_ASSERT((TImplicitlyCastable::Result)); + UNIT_ASSERT(!(TImplicitlyCastable::Result)); + + // pointers + UNIT_ASSERT(!(TImplicitlyCastable, int>::Result)); + UNIT_ASSERT(!(TImplicitlyCastable, ui64>::Result)); + UNIT_ASSERT(!(TImplicitlyCastable, bool>::Result)); // even this + + { + // mostly a compilability test + + THolder a; + UNIT_ASSERT(!a); + UNIT_ASSERT(!bool(a)); + if (a) { + UNIT_ASSERT(false); + } + if (!a) { + UNIT_ASSERT(true); + } + + a.Reset(new TVec); + UNIT_ASSERT(a); + UNIT_ASSERT(bool(a)); + if (a) { + UNIT_ASSERT(true); + } + if (!a) { + UNIT_ASSERT(false); + } + + THolder b(new TVec); + UNIT_ASSERT(a.Get() != b.Get()); + UNIT_ASSERT(a != b); + if (a == b) { + UNIT_ASSERT(false); + } + if (a != b) { + UNIT_ASSERT(true); + } + if (!(a && b)) { + UNIT_ASSERT(false); + } + if (a && b) { + UNIT_ASSERT(true); + } + + // int i = a; // does not compile + // bool c = (a < b); // does not compile + } +} + +void TPointerTest::TestMakeShared() { + { + TSimpleSharedPtr ptr = MakeSimpleShared(5); + UNIT_ASSERT_VALUES_EQUAL(*ptr, 5); + } + { + struct TRec { + int X, Y; + TRec() + : X(1) + , Y(2) + { + } + }; + auto ptr = MakeAtomicShared(); + UNIT_ASSERT_VALUES_EQUAL(ptr->X, 1); + UNIT_ASSERT_VALUES_EQUAL(ptr->Y, 2); + } + { + struct TRec { + int X, Y; + }; + TAtomicSharedPtr ptr = MakeAtomicShared(1, 2); + UNIT_ASSERT_VALUES_EQUAL(ptr->X, 1); + UNIT_ASSERT_VALUES_EQUAL(ptr->Y, 2); + } + { + class TRec { + private: + int X_, Y_; + + public: + TRec(int x, int y) + : X_(x) + , Y_(y) + { + } + + int GetX() const { + return X_; + } + int GetY() const { + return Y_; + } + }; + TSimpleSharedPtr ptr = MakeSimpleShared(1, 2); + UNIT_ASSERT_VALUES_EQUAL(ptr->GetX(), 1); + UNIT_ASSERT_VALUES_EQUAL(ptr->GetY(), 2); + } + { + enum EObjectState { + OS_NOT_CREATED, + OS_CREATED, + OS_DESTROYED, + }; + + struct TObject { + EObjectState& State; + + TObject(EObjectState& state) + : State(state) + { + State = OS_CREATED; + } + + ~TObject() { + State = OS_DESTROYED; + } + }; + + auto throwsException = []() { + throw yexception(); + return 5; + }; + + auto testFunction = [](TSimpleSharedPtr, int) { + }; + + EObjectState state = OS_NOT_CREATED; + try { + testFunction(MakeSimpleShared(state), throwsException()); + } catch (yexception&) { + } + + UNIT_ASSERT(state == OS_NOT_CREATED || state == OS_DESTROYED); + } +} + +template +void TestPtrComparison(const TPtr& ptr) { + UNIT_ASSERT(ptr == ptr); + UNIT_ASSERT(!(ptr != ptr)); + UNIT_ASSERT(ptr == ptr.Get()); + UNIT_ASSERT(!(ptr != ptr.Get())); +} + +void TPointerTest::TestComparison() { + THolder ptr1(new A); + TAutoPtr ptr2; + TSimpleSharedPtr ptr3(new int(6)); + TIntrusivePtr ptr4; + TIntrusiveConstPtr ptr5 = ptr4; + + UNIT_ASSERT(ptr1 != nullptr); + UNIT_ASSERT(ptr2 == nullptr); + UNIT_ASSERT(ptr3 != nullptr); + UNIT_ASSERT(ptr4 == nullptr); + UNIT_ASSERT(ptr5 == nullptr); + + TestPtrComparison(ptr1); + TestPtrComparison(ptr2); + TestPtrComparison(ptr3); + TestPtrComparison(ptr4); + TestPtrComparison(ptr5); +} + +template +void TPointerTest::TestRefCountedPtrsInHashSetImpl() { + THashSet hashSet; + TRefCountedPtr p1(new T()); + UNIT_ASSERT(!IsIn(hashSet, p1)); + UNIT_ASSERT(hashSet.insert(p1).second); + UNIT_ASSERT(IsIn(hashSet, p1)); + UNIT_ASSERT_VALUES_EQUAL(hashSet.size(), 1); + UNIT_ASSERT(!hashSet.insert(p1).second); + + TRefCountedPtr p2(new T()); + UNIT_ASSERT(!IsIn(hashSet, p2)); + UNIT_ASSERT(hashSet.insert(p2).second); + UNIT_ASSERT(IsIn(hashSet, p2)); + UNIT_ASSERT_VALUES_EQUAL(hashSet.size(), 2); +} + +struct TCustomIntrusivePtrOps: TDefaultIntrusivePtrOps { +}; + +struct TCustomDeleter: TDelete { +}; + +struct TCustomCounter: TSimpleCounter { + using TSimpleCounterTemplate::TSimpleCounterTemplate; +}; + +void TPointerTest::TestRefCountedPtrsInHashSet() { + // test common case + TestRefCountedPtrsInHashSetImpl>(); + TestRefCountedPtrsInHashSetImpl>(); + TestRefCountedPtrsInHashSetImpl>(); + TestRefCountedPtrsInHashSetImpl>(); + + // test with custom ops + TestRefCountedPtrsInHashSetImpl>(); + TestRefCountedPtrsInHashSetImpl>(); + TestRefCountedPtrsInHashSetImpl>(); +} + +class TRefCountedWithStatistics: public TNonCopyable { +public: + struct TExternalCounter { + std::atomic Counter{0}; + std::atomic Increments{0}; + }; + + TRefCountedWithStatistics(TExternalCounter& cnt) + : ExternalCounter_(cnt) + { + // Reset counters + ExternalCounter_.Counter.store(0); + ExternalCounter_.Increments.store(0); + } + + void Ref() noexcept { + ++ExternalCounter_.Counter; + ++ExternalCounter_.Increments; + } + + void UnRef() noexcept { + if (--ExternalCounter_.Counter == 0) { + TDelete::Destroy(this); + } + } + + void DecRef() noexcept { + Y_ABORT_UNLESS(--ExternalCounter_.Counter != 0); + } + +private: + TExternalCounter& ExternalCounter_; +}; + +void TPointerTest::TestIntrusiveConstConstruction() { + { + TRefCountedWithStatistics::TExternalCounter cnt; + UNIT_ASSERT_VALUES_EQUAL(cnt.Counter.load(), 0); + UNIT_ASSERT_VALUES_EQUAL(cnt.Increments.load(), 0); + TIntrusivePtr i{MakeIntrusive(cnt)}; + UNIT_ASSERT_VALUES_EQUAL(cnt.Counter.load(), 1); + UNIT_ASSERT_VALUES_EQUAL(cnt.Increments.load(), 1); + i.Reset(); + UNIT_ASSERT_VALUES_EQUAL(cnt.Counter.load(), 0); + UNIT_ASSERT_VALUES_EQUAL(cnt.Increments.load(), 1); + } + { + TRefCountedWithStatistics::TExternalCounter cnt; + UNIT_ASSERT_VALUES_EQUAL(cnt.Counter.load(), 0); + UNIT_ASSERT_VALUES_EQUAL(cnt.Increments.load(), 0); + TIntrusiveConstPtr c{MakeIntrusive(cnt)}; + UNIT_ASSERT_VALUES_EQUAL(cnt.Counter.load(), 1); + UNIT_ASSERT_VALUES_EQUAL(cnt.Increments.load(), 1); + c.Reset(); + UNIT_ASSERT_VALUES_EQUAL(cnt.Counter.load(), 0); + UNIT_ASSERT_VALUES_EQUAL(cnt.Increments.load(), 1); + } +} + +class TVirtualProbe: public NTesting::TProbe { +public: + using NTesting::TProbe::TProbe; + + virtual ~TVirtualProbe() = default; +}; + +class TDerivedProbe: public TVirtualProbe { +public: + using TVirtualProbe::TVirtualProbe; +}; + +class TDerivedProbeSibling: public TVirtualProbe { +public: + using TVirtualProbe::TVirtualProbe; +}; + +void TPointerTest::TestSharedPtrDowncast() { + { + NTesting::TProbeState probeState = {}; + + { + TSimpleSharedPtr base = MakeSimpleShared(&probeState); + UNIT_ASSERT_VALUES_EQUAL(probeState.Constructors, 1); + + { + auto derived = base.As(); + UNIT_ASSERT_VALUES_EQUAL(probeState.Constructors, 1); + + UNIT_ASSERT_VALUES_EQUAL(base.Get(), derived.Get()); + UNIT_ASSERT_VALUES_EQUAL(base.ReferenceCounter(), derived.ReferenceCounter()); + + UNIT_ASSERT_VALUES_EQUAL(base.RefCount(), 2l); + UNIT_ASSERT_VALUES_EQUAL(derived.RefCount(), 2l); + } + + UNIT_ASSERT_VALUES_EQUAL(probeState.Destructors, 0); + } + + UNIT_ASSERT_VALUES_EQUAL(probeState.Destructors, 1); + } + { + NTesting::TProbeState probeState = {}; + + { + TSimpleSharedPtr base = MakeSimpleShared(&probeState); + UNIT_ASSERT_VALUES_EQUAL(probeState.Constructors, 1); + + auto derived = std::move(base).As(); + UNIT_ASSERT_VALUES_EQUAL(probeState.Constructors, 1); + UNIT_ASSERT_VALUES_EQUAL(probeState.CopyConstructors, 0); + UNIT_ASSERT_VALUES_EQUAL(probeState.Destructors, 0); + } + + UNIT_ASSERT_VALUES_EQUAL(probeState.Destructors, 1); + } + { + NTesting::TProbeState probeState = {}; + + { + TSimpleSharedPtr base = MakeSimpleShared(&probeState); + UNIT_ASSERT_VALUES_EQUAL(probeState.Constructors, 1); + + { + auto derivedSibling = base.As(); + UNIT_ASSERT_VALUES_EQUAL(probeState.Constructors, 1); + + UNIT_ASSERT_VALUES_EQUAL(derivedSibling.Get(), nullptr); + UNIT_ASSERT_VALUES_UNEQUAL(base.ReferenceCounter(), derivedSibling.ReferenceCounter()); + + UNIT_ASSERT_VALUES_EQUAL(base.RefCount(), 1l); + UNIT_ASSERT_VALUES_EQUAL(derivedSibling.RefCount(), 0l); + } + + UNIT_ASSERT_VALUES_EQUAL(probeState.Destructors, 0); + } + + UNIT_ASSERT_VALUES_EQUAL(probeState.Destructors, 1); + } + { + NTesting::TProbeState probeState = {}; + + { + TSimpleSharedPtr base = MakeSimpleShared(&probeState); + UNIT_ASSERT_VALUES_EQUAL(probeState.Constructors, 1); + + auto derived = std::move(base).As(); + UNIT_ASSERT_VALUES_EQUAL(derived.Get(), nullptr); + UNIT_ASSERT_VALUES_EQUAL(probeState.Constructors, 1); + UNIT_ASSERT_VALUES_EQUAL(probeState.CopyConstructors, 0); + UNIT_ASSERT_VALUES_EQUAL(probeState.Destructors, 0); + } + + UNIT_ASSERT_VALUES_EQUAL(probeState.Destructors, 1); + } +} diff --git a/util/generic/ptr_ut.pyx b/util/generic/ptr_ut.pyx new file mode 100644 index 00000000000..759681a2cbb --- /dev/null +++ b/util/generic/ptr_ut.pyx @@ -0,0 +1,26 @@ +from libcpp.utility cimport pair +from util.generic.ptr cimport MakeAtomicShared, TAtomicSharedPtr, THolder +from util.generic.string cimport TString +from util.system.types cimport ui64 + +import pytest +import unittest + + +class TestHolder(unittest.TestCase): + + def test_basic(self): + cdef THolder[TString] holder + holder.Reset(new TString("aaa")) + assert holder.Get()[0] == "aaa" + holder.Destroy() + assert holder.Get() == NULL + holder.Reset(new TString("bbb")) + assert holder.Get()[0] == "bbb" + holder.Reset(new TString("ccc")) + assert holder.Get()[0] == "ccc" + + def test_make_atomic_shared(self): + cdef TAtomicSharedPtr[pair[ui64, TString]] atomic_shared_ptr = MakeAtomicShared[pair[ui64, TString]](15, "Some string") + assert atomic_shared_ptr.Get().first == 15 + assert atomic_shared_ptr.Get().second == "Some string" diff --git a/util/generic/queue.cpp b/util/generic/queue.cpp new file mode 100644 index 00000000000..4ebd3f3205a --- /dev/null +++ b/util/generic/queue.cpp @@ -0,0 +1 @@ +#include "queue.h" diff --git a/util/generic/queue.h b/util/generic/queue.h new file mode 100644 index 00000000000..f5959f68f28 --- /dev/null +++ b/util/generic/queue.h @@ -0,0 +1,58 @@ +#pragma once + +#include "fwd.h" +#include "deque.h" +#include "vector.h" +#include "utility.h" + +#include + +#include + +template +class TQueue: public std::queue { + using TBase = std::queue; + +public: + using TBase::TBase; + + inline explicit operator bool() const noexcept { + return !this->empty(); + } + + inline void clear() { + this->c.clear(); + } + + inline S& Container() { + return this->c; + } + + inline const S& Container() const { + return this->c; + } +}; + +template +class TPriorityQueue: public std::priority_queue { + using TBase = std::priority_queue; + +public: + using TBase::TBase; + + inline explicit operator bool() const noexcept { + return !this->empty(); + } + + inline void clear() { + this->c.clear(); + } + + inline S& Container() { + return this->c; + } + + inline const S& Container() const { + return this->c; + } +}; diff --git a/util/generic/queue_ut.cpp b/util/generic/queue_ut.cpp new file mode 100644 index 00000000000..a33399e1048 --- /dev/null +++ b/util/generic/queue_ut.cpp @@ -0,0 +1,210 @@ +#include "queue.h" +#include "list.h" +#include "vector.h" + +#include + +#include + +Y_UNIT_TEST_SUITE(TYQueueTest) { + Y_UNIT_TEST(ConstructorsAndAssignments) { + { + using container = TQueue; + + container c1; + UNIT_ASSERT(!c1); + c1.push(100); + c1.push(200); + UNIT_ASSERT(c1); + + container c2(c1); + + UNIT_ASSERT_VALUES_EQUAL(2, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c2.size()); + + container c3(std::move(c1)); + + UNIT_ASSERT_VALUES_EQUAL(0, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c3.size()); + + c2.push(300); + c3 = c2; + + UNIT_ASSERT_VALUES_EQUAL(3, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(3, c3.size()); + + c2.push(400); + c3 = std::move(c2); + + UNIT_ASSERT_VALUES_EQUAL(0, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(4, c3.size()); + } + + { + using container = TPriorityQueue; + + container c1; + UNIT_ASSERT(!c1); + c1.push(100); + c1.push(200); + UNIT_ASSERT(c1); + + container c2(c1); + + UNIT_ASSERT_VALUES_EQUAL(2, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c2.size()); + + container c3(std::move(c1)); + + UNIT_ASSERT_VALUES_EQUAL(0, c1.size()); + UNIT_ASSERT_VALUES_EQUAL(2, c3.size()); + + c2.push(300); + c3 = c2; + + UNIT_ASSERT_VALUES_EQUAL(3, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(3, c3.size()); + + c2.push(400); + c3 = std::move(c2); + + UNIT_ASSERT_VALUES_EQUAL(0, c2.size()); + UNIT_ASSERT_VALUES_EQUAL(4, c3.size()); + } + } + + Y_UNIT_TEST(pqueue1) { + TPriorityQueue, TLess> q; + + q.push(42); + q.push(101); + q.push(69); + UNIT_ASSERT(q.top() == 101); + + q.pop(); + UNIT_ASSERT(q.top() == 69); + + q.pop(); + UNIT_ASSERT(q.top() == 42); + + q.pop(); + UNIT_ASSERT(q.empty()); + } + + Y_UNIT_TEST(pqueue2) { + using TPQueue = TPriorityQueue, TLess>; + TPQueue q; + + { + TPQueue qq; + + qq.push(42); + qq.push(101); + qq.push(69); + + qq.swap(q); + } + + UNIT_ASSERT(q.top() == 101); + + q.pop(); + UNIT_ASSERT(q.top() == 69); + + q.pop(); + UNIT_ASSERT(q.top() == 42); + + q.pop(); + UNIT_ASSERT(q.empty()); + } + + Y_UNIT_TEST(pqueue3) { + TPriorityQueue, TLess> q; + + q.push(42); + q.push(101); + q.push(69); + q.clear(); + + UNIT_ASSERT(q.empty()); + } + + Y_UNIT_TEST(pqueue4) { + TDeque c; + c.push_back(42); + c.push_back(101); + c.push_back(69); + + TPriorityQueue, TLess> q(TLess(), std::move(c)); + + UNIT_ASSERT(c.empty()); + + UNIT_ASSERT_EQUAL(q.size(), 3); + + UNIT_ASSERT(q.top() == 101); + + q.pop(); + UNIT_ASSERT(q.top() == 69); + + q.pop(); + UNIT_ASSERT(q.top() == 42); + + q.pop(); + UNIT_ASSERT(q.empty()); + } + + Y_UNIT_TEST(queue1) { + TQueue> q; + + q.push(42); + q.push(101); + q.push(69); + UNIT_ASSERT(q.front() == 42); + + q.pop(); + UNIT_ASSERT(q.front() == 101); + + q.pop(); + UNIT_ASSERT(q.front() == 69); + + q.pop(); + UNIT_ASSERT(q.empty()); + } + + Y_UNIT_TEST(queue2) { + using TQueueType = TQueue; + TQueueType q; + + { + TQueueType qq; + + qq.push(42); + qq.push(101); + qq.push(69); + + qq.swap(q); + } + + UNIT_ASSERT(q.front() == 42); + + q.pop(); + UNIT_ASSERT(q.front() == 101); + + q.pop(); + UNIT_ASSERT(q.front() == 69); + + q.pop(); + UNIT_ASSERT(q.empty()); + } + + Y_UNIT_TEST(queue3) { + using TQueueType = TQueue; + TQueueType q; + + q.push(42); + q.push(101); + q.push(69); + q.clear(); + + UNIT_ASSERT(q.empty()); + } +} diff --git a/util/generic/refcount.cpp b/util/generic/refcount.cpp new file mode 100644 index 00000000000..4174858ff98 --- /dev/null +++ b/util/generic/refcount.cpp @@ -0,0 +1 @@ +#include "refcount.h" diff --git a/util/generic/refcount.h b/util/generic/refcount.h new file mode 100644 index 00000000000..01300752aaa --- /dev/null +++ b/util/generic/refcount.h @@ -0,0 +1,168 @@ +#pragma once + +#include +#include +#include + +#include + +template +class TSimpleCounterTemplate: public TCounterCheckPolicy { + using TCounterCheckPolicy::Check; + +public: + inline TSimpleCounterTemplate(long initial = 0) noexcept + : Counter_(initial) + { + } + + inline ~TSimpleCounterTemplate() { + Check(); + } + + inline intptr_t Add(intptr_t d) noexcept { + Check(); + return Counter_ += d; + } + + inline intptr_t Inc() noexcept { + return Add(1); + } + + inline intptr_t Sub(intptr_t d) noexcept { + Check(); + return Counter_ -= d; + } + + inline intptr_t Dec() noexcept { + return Sub(1); + } + + inline bool TryWeakInc() noexcept { + if (!Counter_) { + return false; + } + + Inc(); + Y_ASSERT(Counter_ != 0); + + return true; + } + + inline intptr_t Val() const noexcept { + return Counter_; + } + +private: + intptr_t Counter_; +}; + +class TNoCheckPolicy { +protected: + inline void Check() const { + } +}; + +#if defined(SIMPLE_COUNTER_THREAD_CHECK) + + #include + +class TCheckPolicy { +public: + inline TCheckPolicy() { + ThreadId = SystemCurrentThreadId(); + } + +protected: + inline void Check() const { + Y_ABORT_UNLESS(ThreadId == SystemCurrentThreadId(), "incorrect usage of TSimpleCounter"); + } + +private: + size_t ThreadId; +}; +#else +using TCheckPolicy = TNoCheckPolicy; +#endif + +// Use this one if access from multiple threads to your pointer is an error and you want to enforce thread checks +using TSimpleCounter = TSimpleCounterTemplate; +// Use this one if you do want to share the pointer between threads, omit thread checks and do the synchronization yourself +using TExplicitSimpleCounter = TSimpleCounterTemplate; + +template +struct TCommonLockOps> { + static inline void Acquire(TSimpleCounterTemplate* t) noexcept { + t->Inc(); + } + + static inline void Release(TSimpleCounterTemplate* t) noexcept { + t->Dec(); + } +}; + +class TAtomicCounter { +public: + inline TAtomicCounter(long initial = 0) noexcept + : Counter_(initial) + { + } + + TAtomicCounter(const TAtomicCounter& other) + : Counter_(other.Counter_.load()) + { + } + + TAtomicCounter& operator=(const TAtomicCounter& other) { + Counter_.store(other.Counter_.load()); + return *this; + } + + inline ~TAtomicCounter() = default; + + inline intptr_t Add(intptr_t d) noexcept { + return Counter_ += d; + } + + inline intptr_t Inc() noexcept { + return Add(1); + } + + inline intptr_t Sub(intptr_t d) noexcept { + return Counter_ -= d; + } + + inline intptr_t Dec() noexcept { + return Sub(1); + } + + inline intptr_t Val() const noexcept { + return Counter_.load(); + } + + inline bool TryWeakInc() noexcept { + for (auto curValue = Counter_.load(std::memory_order_acquire);;) { + if (!curValue) { + return false; + } + + if (Counter_.compare_exchange_weak(curValue, curValue + 1)) { + return true; + } + } + } + +private: + std::atomic Counter_; +}; + +template <> +struct TCommonLockOps { + static inline void Acquire(TAtomicCounter* t) noexcept { + t->Inc(); + } + + static inline void Release(TAtomicCounter* t) noexcept { + t->Dec(); + } +}; diff --git a/util/generic/reserve.h b/util/generic/reserve.h new file mode 100644 index 00000000000..81ceed19dc4 --- /dev/null +++ b/util/generic/reserve.h @@ -0,0 +1,11 @@ +#pragma once + +namespace NDetail { + struct TReserveTag { + size_t Capacity; + }; +} + +constexpr ::NDetail::TReserveTag Reserve(size_t capacity) { + return ::NDetail::TReserveTag{capacity}; +} diff --git a/util/generic/scope.cpp b/util/generic/scope.cpp new file mode 100644 index 00000000000..c70d695c1b9 --- /dev/null +++ b/util/generic/scope.cpp @@ -0,0 +1 @@ +#include "scope.h" diff --git a/util/generic/scope.h b/util/generic/scope.h new file mode 100644 index 00000000000..b2c33af61e7 --- /dev/null +++ b/util/generic/scope.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +#include + +namespace NPrivate { + template + class TScopeGuard { + public: + TScopeGuard(const F& function) + : Function_{function} + { + } + + TScopeGuard(F&& function) + : Function_{std::move(function)} + { + } + + TScopeGuard(TScopeGuard&&) = default; + TScopeGuard(const TScopeGuard&) = default; + + ~TScopeGuard() { + Function_(); + } + + private: + F Function_; + }; + + struct TMakeGuardHelper { + template + TScopeGuard operator|(F&& function) const { + return std::forward(function); + } + }; +} + +// \brief `Y_SCOPE_EXIT(captures) { body };` +// +// General implementaion of RAII idiom (resource acquisition is initialization). Executes +// function upon return from the current scope. +// +// @note expects `body` to provide no-throw guarantee, otherwise whenever an exception +// is thrown and leaves the outermost block of `body`, the function `std::terminate` is called. +// @see http://drdobbs.com/184403758 for detailed motivation. +#define Y_SCOPE_EXIT(...) const auto Y_GENERATE_UNIQUE_ID(scopeGuard) Y_DECLARE_UNUSED = ::NPrivate::TMakeGuardHelper{} | [__VA_ARGS__]() mutable -> void + +// \brief `Y_DEFER { body };` +// +// Same as `Y_SCOPE_EXIT` but doesn't require user to provide capture-list explicitly (it +// implicitly uses `[&]` capture). Have same requirements for `body`. +// +// Inspired by `defer` statement in languages like Swift and Go. +// +// \code +// auto item = s.pop(); +// bool ok = false; +// Y_DEFER { if (!ok) { s.push(std::move(item)); } }; +// ... try handle `item` ... +// ok = true; +// \endcode +#define Y_DEFER Y_SCOPE_EXIT(&) diff --git a/util/generic/scope_ut.cpp b/util/generic/scope_ut.cpp new file mode 100644 index 00000000000..bdb434d4875 --- /dev/null +++ b/util/generic/scope_ut.cpp @@ -0,0 +1,47 @@ +#include "scope.h" + +#include +#include + +Y_UNIT_TEST_SUITE(ScopeToolsTest) { + Y_UNIT_TEST(OnScopeExitTest) { + int i = 0; + + { + Y_SCOPE_EXIT(&i) { + i = i * 2; + }; + + Y_SCOPE_EXIT(&i) { + i = i + 1; + }; + } + + UNIT_ASSERT_VALUES_EQUAL(2, i); + } + + Y_UNIT_TEST(OnScopeExitMoveTest) { + THolder i{new int{10}}; + int p = 0; + + { + Y_SCOPE_EXIT(i = std::move(i), &p) { + p = *i * 2; + }; + } + + UNIT_ASSERT_VALUES_EQUAL(20, p); + } + + Y_UNIT_TEST(TestDefer) { + int i = 0; + + { + Y_DEFER { + i = 20; + }; + } + + UNIT_ASSERT_VALUES_EQUAL(i, 20); + } +} diff --git a/tools/enum_parser/enum_serialization_runtime/serialized_enum.cpp b/util/generic/serialized_enum.cpp similarity index 100% rename from tools/enum_parser/enum_serialization_runtime/serialized_enum.cpp rename to util/generic/serialized_enum.cpp diff --git a/tools/enum_parser/enum_serialization_runtime/serialized_enum.h b/util/generic/serialized_enum.h similarity index 92% rename from tools/enum_parser/enum_serialization_runtime/serialized_enum.h rename to util/generic/serialized_enum.h index d50fe3c7bbb..8188c43eede 100644 --- a/tools/enum_parser/enum_serialization_runtime/serialized_enum.h +++ b/util/generic/serialized_enum.h @@ -1,11 +1,10 @@ #pragma once -#include +#include +#include +#include #include -#include -#include -#include #include /* @@ -37,10 +36,10 @@ namespace NEnumSerializationRuntime { template struct TSelectEnumRepresentationType; - template > + template > class TMappedArrayView; - template > + template > class TMappedDictView; } @@ -48,7 +47,7 @@ namespace NEnumSerializationRuntime { template using TMappedDictView = NDetail::TMappedDictView::TType, TValueType>; - /// Class with behaviour similar to std::vector + /// Class with behaviour similar to TVector template using TMappedArrayView = NDetail::TMappedArrayView::TType>; @@ -58,7 +57,7 @@ namespace NEnumSerializationRuntime { * @tparam EnumT enum type */ template - TMappedDictView GetEnumNamesImpl(); + TMappedDictView GetEnumNamesImpl(); /** * Returns unique items in enum or enum class * @@ -73,7 +72,7 @@ namespace NEnumSerializationRuntime { * @tparam EnumT enum type */ template - const std::string& GetEnumAllNamesImpl(); + const TString& GetEnumAllNamesImpl(); /** * Returns C++ identifiers for items in enum or enum class @@ -81,15 +80,15 @@ namespace NEnumSerializationRuntime { * @tparam EnumT enum type */ template - const std::vector& GetEnumAllCppNamesImpl(); + const TVector& GetEnumAllCppNamesImpl(); /** - * Converts @c e to a string. Works like @c ToString(e) function, but returns @c std::string_view instead of @c std::string. + * Converts @c e to a string. Works like @c ToString(e) function, but returns @c TStringBuf instead of @c TString. * Thus works slightly faster and usually avoids any dynamic memory allocation. * @throw yexception is case of unknown enum value */ template - std::string_view ToStringBuf(EnumT e); + TStringBuf ToStringBuf(EnumT e); } /** @@ -98,7 +97,7 @@ namespace NEnumSerializationRuntime { * @tparam EnumT enum type */ template -Y_CONST_FUNCTION ::NEnumSerializationRuntime::TMappedDictView GetEnumNames() { +Y_CONST_FUNCTION ::NEnumSerializationRuntime::TMappedDictView GetEnumNames() { return ::NEnumSerializationRuntime::GetEnumNamesImpl(); } @@ -118,7 +117,7 @@ Y_CONST_FUNCTION ::NEnumSerializationRuntime::TMappedArrayView GetEnumAll * @tparam EnumT enum type */ template -Y_CONST_FUNCTION const std::string& GetEnumAllNames() { +Y_CONST_FUNCTION const TString& GetEnumAllNames() { return ::NEnumSerializationRuntime::GetEnumAllNamesImpl(); } @@ -128,7 +127,7 @@ Y_CONST_FUNCTION const std::string& GetEnumAllNames() { * @tparam EnumT enum type */ template -Y_CONST_FUNCTION const std::vector& GetEnumAllCppNames() { +Y_CONST_FUNCTION const TVector& GetEnumAllCppNames() { return ::NEnumSerializationRuntime::GetEnumAllCppNamesImpl(); } @@ -174,7 +173,7 @@ namespace NEnumSerializationRuntime { } }; - /// Wrapper class with behaviour similar to std::vector + /// Wrapper class with behaviour similar to TVector /// /// @tparam TEnumType enum type at the external interface /// @tparam TRepresentationType designated underlying type of enum @@ -269,7 +268,7 @@ namespace NEnumSerializationRuntime { } // Allocate container and copy view's content into it - template